﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using System.Drawing;
using System.Collections;

using Noris.Schedule.Support;
using Noris.Schedule.Support.Data;
using Noris.Schedule.Support.Sql;
using Noris.Schedule.Support.Core;
using Noris.Schedule.Support.Services;

using Noris.Schedule.Planning.DataFace;
using System.Windows.Forms;

// Tento soubor obsahuje jednotky pro komunikaci plánovacího procesu s vizuální vrstvou
namespace Noris.Schedule.Planning.ProcessData
{
	// MfrPlanningConnectorCls : DATOVÝ KONEKTOR PRO VIZUÁLNÍ TABULI DO PLÁNOVACÍHO PROCESU
	/// <summary>
	/// Konektor do plánovacího procesu ze strany vizuální prezentace.
	/// Poskytuje přístup k potřebným datům plánovacího procesu a vstupní metody pro řízení změn dat.
	/// </summary>
    public partial class MfrPlanningConnectorCls : PlanningComponentCls, IDataSource, IClassTreeExtender, IEvaluationDataSource, IDisposable
	{
		#region KONSTRUKCE
		/// <summary>
		/// Konstruktor, který vytvoří default testovací parametry procesu, a vytvoří si plánovací objekt.
		/// </summary>
		/// <param name="processData"></param>
		public MfrPlanningConnectorCls()
			: base(null)
		{   // Není voláno
			this._PlanningData = new PlanningDataCls(null);
            this._PlanningData.MfrConnector = this;
		}
		/// <summary>
		/// Konstruktor, který vytvoří default testovací parametry procesu, a vytvoří si plánovací objekt.
		/// </summary>
		/// <param name="processData"></param>
		internal MfrPlanningConnectorCls(PlanningParametersCls planningParameters)
			: base(null)
		{   // je voláno z deklarátoru grafů, tedy pro ostrý provoz
            this._PlanningData = new PlanningDataCls(null);
            this._PlanningData.MfrConnector = this;
            this.PlanningData.PlanningParameters = planningParameters;
		}
		/// <summary>
		/// Konstruktor, který převezme sestavený objekt plánovacího procesu. Víceméně pro testy.
		/// </summary>
		/// <param name="processData"></param>
		internal MfrPlanningConnectorCls(PlanningDataCls processData) : base(null)
		{   // je voláno z testů
			this._PlanningData = processData;
            this._PlanningData.MfrConnector = this;
        }
		public void Dispose()
		{
			if (this._PlanningData != null) this._PlanningData.Dispose();
            this._PlanningData = null;

            if (this.__WorkQueue != null) this.__WorkQueue.Dispose();
            this.__WorkQueue = null;
		}
		#endregion
		#region PŘÍSTUP DO POTŘEBNÝCH BLOKŮ PLÁNOVACÍHO PROCESU
		/// <summary>
		/// PlanningData = plánovací základna
		/// </summary>
		public PlanningDataCls PlanningData { get { return this._PlanningData; } }
		private PlanningDataCls _PlanningData;
		/// <summary> { get; }
		/// Data jednotlivých plánovacích os (osa materiálu, přehled kapacitních úkolů, systém vztahů)
		/// </summary>
		public AxisHeapCls AxisHeap { get { return this.PlanningData.AxisHeap; } }
		/// <summary> { get; }
		/// Data o probíhající výrobě (příkazy, operace, komponenty, zdroje)
		/// </summary>
		internal ProductDataCls ProductData { get { return this.PlanningData.ProductData; } }
		/// <summary> { get; }
		/// Data o materiálu (zásoby, materiálové osy)
		/// </summary>
		internal MaterialDataCls MaterialData { get { return this.PlanningData.MaterialData; } }
		/// <summary> { get; }
		/// Data o kapacitách (zdroje, kapacitní jednotky, kalendáře), registrační systém
		/// </summary>
		internal CapacityRegisterCls CapacityRegisterData { get { return this.PlanningData.CapacityRegisterData; } }
		/// <summary> { get; }
		/// Systém pro správu kapacitních jednotek, zdrojů kapacit a vztahy mezi nimi
		/// </summary>
		public CapacityDataCls CapacityData { get { return this.PlanningData.CapacityData; } }
		#endregion
		#region PUBLIC METODY : ZAŘAZENÍ POŽADAVKU DO FRONTY PRÁCE
		/// <summary>
		/// Vlastnosti datového zdroje. Jde o statické informace.
		/// </summary>
		DataSourceProperties IDataSource.Properties
		{
			get
			{
				DataSourceProperties properties = new DataSourceProperties();
				properties.IsAsynchronous = true;
				properties.MakeRelationMap = true;
				properties.SourceName = "Plánování výrobních kapacit";
				return properties;
			}
		}
		/// <summary>
		/// Aktuální informace, zda datový zdroj může přijmout asynchronní požadavek.
		/// Má vrátit true tehdy, pokud je na to naprogramován,
		/// a současně v delegátu this.RequestCompleted je odkaz na metodu, kterou je možno volat po dokončení requestu.
		/// </summary>
		bool IDataSource.AcceptRequestAsync { get { return (this._RequestCompleted != null); } }
		/// <summary>
		/// Zařadí požadavek do fronty práce, hned na její začátek, a počká na dokončení.
		/// Výsledky procesu jsou uloženy v parametru request.
		/// Po dokončení není volán delegát RequestCompleted.
		/// </summary>
		/// <param name="request">Požadovaná operace</param>
		void IDataSource.RunRequestSync(DataSourceRequest request)
		{
			this._WorkQueue.AddRequestSync(request);

			if (request.ResultException != null)
			{
				Dialog.ShowError(request.ResultException);
				// System.Windows.Forms.Clipboard.SetText(request.ResultException.ToString());
				// MessageBox.Show("Došlo k chybě. Chyba je zachycena ve Windows schránce.\r\nVložte ji do e-mailu klávesou Ctrl+V.\r\n\r\n" + request.ResultException.ToString(), "Chyba", MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
		}
		/// <summary>
		/// Zařadí požadavek do fronty práce, nečeká na její dokončení, ihned vrací řízení.
		/// Po dokončení jsou výsledky odeslány do delegáta RequestCompleted.
		/// Výsledky procesu jsou do něj předány v parametru request.
		/// Pokud není vyplněn delegát RequestCompleted, je vyvolána chyba.
		/// </summary>
		/// <param name="request">Požadovaná operace</param>
		void IDataSource.RunRequestAsync(DataSourceRequest request)
		{
			if (this._RequestCompleted == null)
				Throw.SysError(MessageInfo.Get("Byl proveden pokus o použití asynchronní metody DataSource.RunRequestAsync v datovém zdroji, kde není definován delegát RequestCompleted."));
			this._WorkQueue.AddRequestAsync(request);
		}
		/// <summary>
		/// Metoda upřednostní požadavky aktivního grafu na úkor požadavků neaktivních grafů.
		/// Jinými slovy, zajistí, že asynchronní požadavky, která vydal aktivní graf, se budou realizovat dříve, než požadavky jiných grafů.
		/// Metoda se typicky volá po přepnutí aktivního grafu. Zajistí, že požadavky nyní aktivního grafu se vyřídí dříve, 
		/// než dojde řada na vyřizování nevyřízených požadavků z grafů, které nejsou aktivní.
		/// </summary>
		/// <param name="activeGraphId"></param>
		void IDataSource.ActivateAsyncRequestForGraphId(int activeGraphId)
		{
			this._WorkQueue.ScanAllRequest(delegate(DataSourceRequest request) { request.SetActiveGraph(activeGraphId); });
		}
		/// <summary>
		/// Řízení asynchronní operace datového zdroje, umožní pozastavit / rozběhnout asynchronní operace.
		/// false = default = asynchronní operace normálně probíhají / true = asynchronní operace jsou pozastaveny.
		/// Pokud datový zdroj nepodporuje asynchronní operace (deklaruje CanBeAsynchronous = false), nechť implementuje standardní property bez rozšířené funkcionality.
		/// Pokud datový zdroj podporuje asynchronní operace, může pro jejich řízení využít třídu Aps.Core.WorkQueueCls, kde je property WaitAsyncQueue řádně implementována.
		/// Pak stačí propojit property IDataSource.WaitAsyncQueue na WorkQueueCls.WaitAsyncQueue
		/// </summary>
		bool IDataSource.WaitAsyncQueue { get { return this._WorkQueue.WaitAsyncQueue; } set { this._WorkQueue.WaitAsyncQueue = value; } }
		/// <summary>
		/// Vrací počet čekajících asynchronních operací, typicky pro zobrazení ve stavovém řádku.
		/// Pokud datový zdroj nepodporuje asynchronní operace (deklaruje CanBeAsynchronous = false), nechť vrátí 0.
		/// Pokud datový zdroj podporuje asynchronní operace, může pro jejich řízení využít třídu Aps.Core.WorkQueueCls, kde je property AsynchronousOperationCount řádně implementována.
		/// Pak stačí propojit property IDataSource.AsynchronousOperationCount na WorkQueueCls.AsynchronousOperationCount
		/// </summary>
		int IDataSource.AsynchronousOperationCount { get { return this._WorkQueue.AsynchronousOperationCount; } }
		/// <summary>
		/// Delegát na metodu, která bude spuštěna vždy poté, kdy je dokončena asynchronní práce na jednom každém požadavku.
		/// Metoda dostává jako parametr ten požadavek, který byl předán k vyřízení.
		/// Požadavek má vyplněné Result údaje.
		/// Tento delegát se nevolá pro synchronní požadavky.
		/// </summary>
		Action<DataSourceRequest> IDataSource.RequestCompleted { get { return _RequestCompleted; } set { _RequestCompleted = value; } }
		private Action<DataSourceRequest> _RequestCompleted;
		#endregion
		#region FRONTA PRÁCE - IMPLEMENTACE, VÝKONNÉ A NOTIFIKAČNÍ METODY, ZPRACOVÁNÍ JEDNOTLIVÉHO POŽADAVKU (AŽ NA NĚJ PŘIJDE ČAS)
		/// <summary>
		/// Místní implementace fronty práce. V get metodě se v případě potřeby založí nová fronta.
		/// </summary>
        private WorkQueueCls<DataSourceRequest> _WorkQueue
        {
            get
            {
                if (__WorkQueue == null)
                {
                    lock (__WorkQueueLock)
                    {
                        if (__WorkQueue == null)
                        {
                            this.__WorkQueue = new WorkQueueCls<DataSourceRequest>(this._DoWork, this._NotifyDone);
                            this.__WorkQueue.WorkQueueCountChanged += new WorkQueueCountChangedDelegate(this._WorkQueueCountChanged);
                        }
                    }
                }
                return this.__WorkQueue;
            }
        }
        private WorkQueueCls<DataSourceRequest> __WorkQueue;
        private object __WorkQueueLock = new object();
        /// <summary>
		/// Pracovitá metoda: je vyvolávaná z fronty práce tehdy,
		/// když v ní bude dokončeno zpracování jednoho požadavku a přijde čas na zpracování dalšího požadavku.
		/// </summary>
		/// <param name="request"></param>
		private void _DoWork(DataSourceRequest request)
		{
			try
			{
				this._RunRequest(request);
			}
			catch (Exception exc)
			{
				request.ResultException = exc;
			}
		}
		/// <summary>
		/// Ohlášení dokončení jedné práce, odsud do nadřízeného delegáta.
		/// Metodu volá fronta práce poté, kdy se jí vrátí řízení z jednoho požadavku.
		/// Metoda zajistí vyvolání eventu do nadřízeného objektu.
		/// </summary>
		/// <param name="request"></param>
		private void _NotifyDone(DataSourceRequest request)
		{
			if (this._RequestCompleted != null)
				this._RequestCompleted(request);
		}
		#endregion
		#region EVENTY
		/// <summary>
		/// Event, který se volá po každé změně počtu asynchronních požadavků
		/// </summary>
		public event WorkQueueCountChangedDelegate AsyncRequestCountChanged;
		/// <summary>
		/// Invoker eventu WorkQueueCountChanged
		/// </summary>
		/// <param name="count"></param>
		/// <param name="increased"></param>
		protected virtual void OnAsyncRequestCountChanged(object sender, WorkQueueCountChangedEventArgs e)
		{
			if (AsyncRequestCountChanged != null)
				AsyncRequestCountChanged(sender, e);
		}
		/// <summary>
		/// Handler události _WorkQueueCountChanged vyvolané z _WorkQueue
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		void _WorkQueueCountChanged(object sender, WorkQueueCountChangedEventArgs e)
		{
			this.OnAsyncRequestCountChanged(sender, e);         // Jen předám dál
		}
		#endregion
		#region RUN REQUEST : FYZICKÝ ROZCESTNÍK JEDNOTLIVÝCH POŽADAVKŮ
		/// <summary>
		/// Provedení požadovaného requestu
		/// </summary>
		/// <param name="request"></param>
		private void _RunRequest(DataSourceRequest request)
		{
			// Chyba v připojovacím profilu:
			if (!Steward.ConnectReady)
			{
				return;
			}

			// Uložíme si verzi plánu z requestu do parametrů procesu, do property RequestPlanCVersionData:
			if (request.DataVersion != null && request.DataVersion is PlanCVersionHdrCls)
			{	// V requestu je uvedena verze plánu. Měla by tam být vždy, ale kdo ví...
                this.PlanningData.PlanningParameters.RequestPlanCVersionData = request.DataVersion as PlanCVersionHdrCls;
			}

			// Rozcestník podle požadavku:
			switch (request.RequestType)
			{
				case DataSourceRequestType.TopLevelRead:
				case DataSourceRequestType.SubRowsRead:
					this._ReadRows(request);
					break;
				case DataSourceRequestType.ElementsRead:
					this._ReadElements(request);
					break;
				case DataSourceRequestType.ElementsMapRead:
					this._ReadElementsMap(request);
					break;
				case DataSourceRequestType.CreateDataRelationNet:
					this._CreateRelations(request);
					break;
				case DataSourceRequestType.CreateDataLinkNet:
					this._CreateLinkList(request);
					break;
                case DataSourceRequestType.EventBeforeEdit:
                    this._BeforeEditHandler(request);
                    break;
                case DataSourceRequestType.EventAfterEdit:
					this._AfterEditHandler(request);
					break;
				case DataSourceRequestType.FindInterGraphTargetData:
					this._FindInterGraphTargetData(request);
					break;
				case DataSourceRequestType.QueryDataVersion:
					this._QueryDataVersion(request);
					break;
				case DataSourceRequestType.QueryAboutRequest:
					this._QueryAboutRequest(request);
					break;
				case DataSourceRequestType.ActivateDataVersion:
					this._ActivateDataVersion(request);
					break;
				case DataSourceRequestType.RunDataFunction:
					this._RunDataFunction(request);
					break;
				case DataSourceRequestType.DisconnectDataProfile:
					this._DisconnectDataProfile(request);
					break;
				case DataSourceRequestType.CheckDataProfile:
					this._CheckDataProfile(request);
					break;
			}
		}
		#endregion
		#region ŘÍZENÍ PROCESU
		/// <summary>
		/// Metoda zjistí, jaké verze kapacitního plánu v této databázi existují, a vrátí jejich seznam.
		/// Seznam se nabízí v toolbaru.
		/// </summary>
		/// <param name="requestInput">Vstupující požadavek</param>
		private void _QueryDataVersion(DataSourceRequest requestInput)
		{
			DataSourceRequestGetDataVersions request = DataSourceRequest.TryGetTypedRequest<DataSourceRequestGetDataVersions>(requestInput);

			List<StringTag> versionList;
			StringTag versionCurrent;
			this.PlanningData.GetDataVersionList(out versionList, out versionCurrent);
			request.QueryResultDataVersionList = versionList;
			request.QueryResultDataVersionCurrent = versionCurrent;
		}
		/// <summary>
		/// Vyplní metadata o dotazovaném procesu
		/// </summary>
		/// <param name="requestInput">Vstupující požadavek</param>
		private void _QueryAboutRequest(DataSourceRequest requestInput)
		{
			DataSourceRequestQuery request = DataSourceRequest.TryGetTypedRequest<DataSourceRequestQuery>(requestInput);
			switch (request.QueryRequestType)
			{	// Na jaký proces se nás ptají?
				case DataSourceRequestType.ActivateDataVersion:
					this.PlanningData.QueryAboutActiveDataVersion(request);
					break;
				case DataSourceRequestType.RunDataFunction:
					this.PlanningData.QueryAboutDataFunction(request);
					break;
			}
		}
		/// <summary>
		/// Metoda pro přepnutí verze dat
		/// </summary>
		/// <param name="requestInput">Vstupující požadavek</param>
		private void _ActivateDataVersion(DataSourceRequest requestInput)
		{
			DataSourceRequestDataFunction request = DataSourceRequest.TryGetTypedRequest<DataSourceRequestDataFunction>(requestInput);
			this.PlanningData.RunActivateDataVersion(request);
		}
		/// <summary>
		/// Metoda pro spuštění datové funkce
		/// </summary>
		/// <param name="requestInput">Vstupující požadavek</param>
		private void _RunDataFunction(DataSourceRequest requestInput)
		{
			DataSourceRequestDataFunction request = DataSourceRequest.TryGetTypedRequest<DataSourceRequestDataFunction>(requestInput);
			this.PlanningData.RunDataFunction(request);
		}
		/// <summary>
		/// Zajistí odpojení od databáze. Nyní bez dotazů, bez ukládání dat.
		/// Ukládání si řeší metoda _QueryDataVersion() tak, že předřadí request _SourceDataSaveAll (anebo nahodí Refuse = true).
		/// </summary>
		private void _DisconnectDataProfile(DataSourceRequest request)
		{
            this.PlanningData.DisconnectDataProfile(request);
		}
		/// <summary>
		/// Ověří, zda aktuální data jsou připojitelná (korektní).
		/// </summary>
		/// <param name="requestInput"></param>
		private void _CheckDataProfile(DataSourceRequest requestInput)
		{
			DataSourceRequestDataFunction request = DataSourceRequest.TryGetTypedRequest<DataSourceRequestDataFunction>(requestInput);
			request.ResultCorrect = this.PlanningData.RunDataTestCurrent();
		}
		#endregion
		#region ČTENÍ DAT Z PROCESU DO GUI - ŘÁDKY
		/// <summary>
		/// Vstupní bod požadavku na čtení dat řádků
		/// </summary>
		/// <param name="requestInput">Vstupující požadavek</param>
		private void _ReadRows(DataSourceRequest requestInput)
		{
			if (this.PlanningData.CurrentStateCanGetData)
			{
				DataSourceRequestReadRows request = DataSourceRequest.TryGetTypedRequest<DataSourceRequestReadRows>(requestInput);
				if (request.RequestRowGId.AllRecords)
					this._ReadRowsAll(request);
				else
					this._ReadRowsSub(request);
			}
		}
		/// <summary>
		/// Vstupní bod požadavku na čtení dat všech řádků (základní seznam)
		/// </summary>
		/// <param name="request"></param>
		private void _ReadRowsAll(DataSourceRequestReadRows request)
		{
			switch (request.RequestRowGId.ClassNumber)
			{
				case PlanUnitSAxisCls.ClassNr:
					this._ReadRowsAllPlanAxisS(request);
					// this._ReadRowsAllPlanAxisS_Old(request);
					break;
				case PlanUnitCCls.ClassNr:
					this._ReadRowsAllPlanUnitC(request);
					break;
			}
		}
		/// <summary>
		/// Vstupní bod požadavku na čtení dat podřízených řádků (jedna podúroveň)
		/// </summary>
		/// <param name="request"></param>
		private void _ReadRowsSub(DataSourceRequestReadRows request)
		{
			switch (request.RequestRowGId.ClassNumber)
			{
				case PlanUnitSAxisCls.ClassNr:
					//this._ReadRowsSubPlanAxisS(request);
					throw new NotImplementedException("Volání metody _ReadRowsSub()");
                case Constants.ClassNumberAxis:
                    this._ReadRowsOnePlanAxisS(request);
                    break;
			}
		}
		/// <summary>
		/// Vstupní bod požadavku na čtení dat všech řádků kapacitních jednotek.
		/// </summary>
		/// <param name="request"></param>
		private void _ReadRowsAllPlanUnitC(DataSourceRequestReadRows request)
		{
            List<CapacityUnitCls> unitList = new List<CapacityUnitCls>(this.CapacityData.CapacityUnitList);
            unitList.Sort((a, b) => String.Compare(a.ReferNazev, b.ReferNazev, StringComparison.CurrentCultureIgnoreCase));
			foreach (CapacityUnitCls unit in unitList)
			{
				PlanUnitCCls planUnitC = unit.PlanUnitData;
				PlanningVisualDataRowCls item = new PlanningVisualDataRowCls(planUnitC.GId, request.RequestedGraphMode, planUnitC.Reference, planUnitC.Nazev, false);
				item.ActionOnDoubleClick = RowActionType.FindRowOnNextGraph;
				request.ResultItems.Add(item);
			}
		}
		#endregion
		#region NAČÍTÁNÍ ŘÁDKŮ OSY S : MAIN ŘÁDKY (POPTÁVKY) A PODŘÍZENÉ ŘÁDKY (VÝROBA DÍLCŮ, ZÍSKÁNÍ MATERIÁLU), VČETNĚ TVORBY ELEMENTŮ (ČASY)
		#region Public metody pro načtení řádků osy S pro vyjmenované řádky (finály)
        /// <summary>
        /// Metoda načte a vrátí seznam řádků osy S do grafu Gantt, které reprezentují daný záznam osy S (finální řádek, v Root úrovni grafu) 
        /// a všechny jeho vizuální podřízené (výroba jednotlivých dílců).
        /// Vstupem je GID finálního řádku.
        /// Finální řádek pro libovolný GID lze najít metodou .
        /// </summary>
        /// <param name="finalAxisGId"></param>
        /// <returns></returns>
        public IEnumerable<IDataRow> ReadRowsForAxisS(GID finalAxisGId)
        {
            return this._ReadRowsForAxisS(new GID[] { finalAxisGId }, false);
        }
        /// <summary>
        /// Metoda načte a vrátí seznam řádků osy S do grafu Gantt, které reprezentují daný záznam osy S (finální řádek, v Root úrovni grafu) 
        /// a všechny jeho vizuální podřízené (výroba jednotlivých dílců).
        /// Vstupem je GID finálního řádku.
        /// Finální řádek pro libovolný GID lze najít metodou .
        /// </summary>
        /// <param name="finalAxisGIds"></param>
        /// <returns></returns>
        public IEnumerable<IDataRow> ReadRowsForAxisS(IEnumerable<GID> finalAxisGIds)
        {
            return this._ReadRowsForAxisS(finalAxisGIds, false);
        }
        /// <summary>
        /// Metoda načte a vrátí seznam řádků osy S do grafu Gantt, které reprezentují daný záznam osy S (finální řádek, v Root úrovni grafu) 
        /// a všechny jeho vizuální podřízené (výroba jednotlivých dílců).
        /// Vstupem je GID finálního řádku.
        /// Finální řádek pro libovolný GID lze najít metodou .
        /// </summary>
        /// <param name="axisGId"></param>
        /// <param name="findFinal">Požadavek na dohledání finálů</param>
        /// <returns></returns>
        public IEnumerable<IDataRow> ReadRowsForAxisS(GID axisGId, bool findFinal)
        {
            return this._ReadRowsForAxisS(new GID[] { axisGId }, findFinal);
        }
        /// <summary>
        /// Metoda načte a vrátí seznam řádků osy S do grafu Gantt, které reprezentují daný záznam osy S (finální řádek, v Root úrovni grafu) 
        /// a všechny jeho vizuální podřízené (výroba jednotlivých dílců).
        /// Vstupem je GID finálního řádku.
        /// Finální řádek pro libovolný GID lze najít metodou .
        /// </summary>
        /// <param name="axisGIds"></param>
        /// <param name="findFinal">Požadavek na dohledání finálů</param>
        /// <returns></returns>
        public IEnumerable<IDataRow> ReadRowsForAxisS(IEnumerable<GID> axisGIds, bool findFinal)
        {
            return this._ReadRowsForAxisS(axisGIds, findFinal);
        }
        /// <summary>
        /// Metoda volitelně najde finální záznamy osy S, a pro ně vytvoří seznam řádků do grafu Gantt + odpovídající podřízené řádky
        /// </summary>
        /// <param name="axisGIds"></param>
        /// <param name="findFinal"></param>
        /// <returns></returns>
        private IEnumerable<IDataRow> _ReadRowsForAxisS(IEnumerable<GID> axisGIds, bool findFinal)
        {
            IEnumerable<GID> finalAxisGIds = null;
            if (findFinal)
                finalAxisGIds = this._ReadRowsFindFinalAxisS(axisGIds);
            else
                finalAxisGIds = axisGIds;

            return this._ReadRowsForFinalAxisS(finalAxisGIds);
        }
        /// <summary>
        /// Metoda k daným záznamům (libovolný GID v AxisHeap: task nebo axis) dohledá finální záznamy osy S = takové, které jsou v Root úrovni grafu Osa S - Gantt.
        /// </summary>
        /// <param name="itemGIds"></param>
        /// <returns></returns>
        private IEnumerable<GID> _ReadRowsFindFinalAxisS(IEnumerable<GID> itemGIds)
        {
            Dictionary<GID, object> scanned = new Dictionary<GID, object>();
            Dictionary<GID, object> result = new Dictionary<GID, object>();
            foreach (GID itemGId in itemGIds)
            {
                if (!scanned.ContainsKey(itemGId))
                {
                    List<GID> finalGIds = this.AxisHeap.FindRelationFinalAxisSGIds(itemGId);
                    scanned.Add(itemGId, null);
                    foreach (GID finalGId in finalGIds)
                    {
                        if (!result.ContainsKey(finalGId))
                            result.Add(finalGId, null);
                    }
                }
            }
            return result.Keys;
        }
        /// <summary>
        /// Metoda pro finální záznamy osy S vytvoří seznam řádků do grafu Gantt + odpovídající podřízené řádky
        /// </summary>
        /// <param name="finalAxisGIds"></param>
        /// <returns></returns>
        private IEnumerable<IDataRow> _ReadRowsForFinalAxisS(IEnumerable<GID> finalAxisGIds)
        {
            if (finalAxisGIds == null) return new List<IDataRow>();
            Dictionary<GID, object> loaded = new Dictionary<GID, object>();
            List<IDataRow> result = new List<IDataRow>();
            foreach (GID finalAxisGId in finalAxisGIds)
            {
                if (!loaded.ContainsKey(finalAxisGId))
                {
                    IEnumerable<IDataRow> list = this._ReadRowsGetPlanAxisSOneList(finalAxisGId, null);
                    if (list != null)
                        result.AddRange(list);
                    loaded.Add(finalAxisGId, null);
                }
            }
            return result;
        }
        #endregion
        #region Privátní metody pro fyzickou tvorbu IDataRow
        /// <summary>
        /// Metoda načte a vrátí všechny řádky osy S + podřízené dílce + jejich materiál, které se mají zobrazit v grafu Poptávky.Gantt
        /// </summary>
        /// <returns></returns>
        internal List<IDataRow> ReadRowsGetAllPlanAxisS()
        {
            return this._ReadRowsGetAllPlanAxisS(null);
        }
        /// <summary>
        /// Metoda načte a vrátí všechny řádky osy S + podřízené dílce + jejich materiál, které se mají zobrazit v grafu Poptávky.Gantt
        /// </summary>
        /// <returns></returns>
        internal List<IDataRow> ReadRowsGetAllPlanAxisS(Func<MaterialPlanAxisItemCls, bool> filter)
        {
            return this._ReadRowsGetAllPlanAxisS(filter);
        }
        /// <summary>
		/// Vstupní bod požadavku na čtení dat všech řádků plánovací osy S
		/// </summary>
		/// <param name="request"></param>
		private void _ReadRowsAllPlanAxisS(DataSourceRequestReadRows request)
		{
            request.ResultItems = this._ReadRowsGetAllPlanAxisS(null);
		}
        /// <summary>
        /// Metoda fyzicky sestaví seznam všech řádků  osy S + podřízené dílce + jejich materiál, které se mají zobrazit v grafu Poptávky.Gantt. Seznam vrátí.
        /// </summary>
        /// <returns></returns>
        private List<IDataRow> _ReadRowsGetAllPlanAxisS(Func<MaterialPlanAxisItemCls, bool> filter)
        {
            List<IDataRow> result = new List<IDataRow>();

            // Získám soupis vztahů, které popisují výdej z položek osy S do skladových poptávek:
            GID outputGId = new GID(Constants.ClassNumberOutput, 0);      // GID třídy Výstup z výroby
            List<PlanningItemRelationCls> outRelList = this.AxisHeap.FindRelationPrevIDData(outputGId);
            foreach (PlanningItemRelationCls outRel in outRelList)
            {
                // Pojistka:
                if (outRel.Relation != PlanningItemRelationType.AxisOutput)
                    continue;

                // Pro jeden záznam osy S najdeme její data, data jejích podřízených tasků, 
                // sestavíme řádky, do nich vložíme elementy a vše uložíme do requestu:
                IEnumerable<IDataRow> rowList = this._ReadRowsGetPlanAxisSOneList(outRel.ItemGIDPrev, filter);
                result.AddRange(rowList);
            }

            // Setřídím podle obsahu prvního sloupce:
            this._ReadRowsAllPlanAxisSSort(ref result);

            return result;
        }
        /// <summary>
        /// Načtení jednoho main řádku osy S (top level)
        /// plus jeho podřízené řádky, na explicitní požadavek grafu
        /// </summary>
        /// <param name="request"></param>
        private void _ReadRowsOnePlanAxisS(DataSourceRequestReadRows request)
        {
            List<IDataRow> itemList = new List<IDataRow>();

            // Pro jeden záznam osy S najdeme její data, data jejích podřízených tasků, 
            // sestavíme řádky, do nich vložíme elementy a vše uložíme do requestu:
            MaterialPlanAxisItemCls axis = this.AxisHeap.FindAxisSItem(request.RequestRowGId);
            IEnumerable<IDataRow> rowList = this._ReadRowsGetPlanAxisSOneList(axis.AxisGID, null);
            itemList.AddRange(rowList);

            request.ResultItems = itemList;
        }
		/// <summary>
		/// Setřídí daný seznam: prvky hlavní úrovně seznamu setřídí podle reference a názvu, prvky podřízené netřídí.
		/// </summary>
		/// <param name="itemList"></param>
		private void _ReadRowsAllPlanAxisSSort(ref List<IDataRow> itemList)
		{
			List<IDataRow> listMain = new List<IDataRow>();
			List<IDataRow> listSubs = new List<IDataRow>();
			foreach (IDataRow item in itemList)
			{
				if (item.ParentGId.IsEmpty)
					listMain.Add(item);
				else
					listSubs.Add(item);
			}
			listMain.Sort(PlanningVisualDataRowCls.CompareByLevelReferNazev);
			listMain.AddRange(listSubs);

			itemList = listMain;
		}
		/// <summary>
		/// Pro jeden záznam osy S najde její data, a data jejích podřízených tasků,
		/// sestaví řádky, do nich vloží elementy a vše uloží do requestu.
		/// </summary>
		/// <param name="axisGId">GID řádku = záznam osy S, pro který chcem najít a vytvořit grafické řádky (za něj a za všechny jeho podřízené)</param>
        private IEnumerable<IDataRow> _ReadRowsGetPlanAxisSOneList(GID axisGId, Func<MaterialPlanAxisItemCls, bool> filter)
		{
			List<IDataRow> result = new List<IDataRow>();

			// Záznam osy S, vytvořit řádek do grafu:
			MaterialPlanAxisItemCls axis = this._FindItemAxisS(axisGId);
			if (axis == null) return result;
			PlanningVisualDataRowCls rowMain = this.PlanningData.VisualGetDataRowForAxisSGanttMain(axis);
			if (rowMain == null) return result;

			// Obdobný algoritmus je v metodě this._ReadElementsAxisS().
			// Prostor pro tvorbu podřízených řádků, souhrn jejich času:
			List<IDataRow> subList = new List<IDataRow>();
			VisualTaskTimeCls parentTime = VisualTaskTimeCls.CreateFromEnd(axis.DatePlanEnd);

			// Vygenerovat soupis podřízených řádků:
			this._RowAxisSAddSubTasks(axis.AxisGID, filter, true, ref parentTime, ref subList);

			// Do Main řádku osy (rowMain) vložím její sumární čas:
			this._RowAxisSAddElementMain(rowMain, axis, parentTime);
			rowMain.AllElementsLoaded = true;
			rowMain.HasSubNodes = (subList.Count > 0);

			// Provedu modifikaci elementu Main řádku, pokud se jedná o položku, 
			//  která nezatěžuje výrobu (výdej do poptávky ze skladové zásoby):
			this._RowAxisSMainRowAdjust(rowMain, axis, parentTime, subList);

			// Pokud osa S vyhovuje filtru, pak řádek Main vložím do výsledného soupisu:
            if (_AxisSComplyFilter(axis, filter))
    			result.Add(rowMain);

			// Do výsledného soupisu řádků vepíšu ještě všechny vyfiltrované podřízené záznamy:
			if (subList.Count > 0)
				result.AddRange(subList);

			return result;
		}
		/// <summary>
		/// V případě potřeby upraví data v hlavním řádku osy S.
		/// Jde o situaci, kdy na řádku je výdej do poptávky ze skladu, kdy není žádná výroba a řádek obsahuje jeden element s nulovým časem (čas výdeje).
		/// </summary>
		/// <param name="rowMain"></param>
		/// <param name="axis"></param>
		/// <param name="parentTime"></param>
		/// <param name="subList"></param>
		private void _RowAxisSMainRowAdjust(PlanningVisualDataRowCls rowMain, MaterialPlanAxisItemCls axis, VisualTaskTimeCls parentTime, List<IDataRow> subList)
		{
			// if (parentTime.TimeReal.Seconds > 0D && subList.Count > 0)
			//	return;
			
			// Pokud je v hlavním řádku nějaký element s časem větším než nula, pak zdejší úprava není nutná:
			if (rowMain.ElementList.Count > 0 && rowMain.ElementList[0].TimeRange.Seconds > 0D)
				return;

			// Pokud element [0] má čas == 0, pak bych měl tento element zobrazit jinak:
			IDataElement element = rowMain.ElementList[0];
			if (element is PlanningVisualDataElementCls)
			{
				PlanningVisualDataElementCls visElement = element as PlanningVisualDataElementCls;
				visElement.ElementShapeType = GraphElementShapeType.RightTriangle;
			}
			TimeRange current = rowMain.ElementList[0].TimeRange;
			rowMain.ElementList[0].TimeRange = new TimeRange(current.Begin.AddSeconds(-1D), current.End);

		}
		/// <summary>
		/// Tato metoda zajistí nalezení a načtení dat pro jeden řádek osy S a vytvoření jeho standardní grafické reprezentace.
		/// Řádek tedy obsahuje i potřebné elementy (a to buď jako Main row, nebo Sub row včetně odkazu na Main row).
		/// Postup: pro daný řádek se vyhledá jeho nejvyšší nadřízený (koncový příjem), pro něj se provede standardní rozpad,
		/// a z tohoto rozpadu se zpětně najde a vrátí požadovaný řádek.
		/// Tím je zajištěno, že vrácený řádek má identický vzhled jako když se generuje pro přvotním načítání, 
		/// i včetně dat "nahoru" (nadřízený) a dolů (skladba časů).
		/// </summary>
		/// <param name="requestGId">Požadovaný záznam osy S, pozor_ může nést číslo třídy ClassNumberAxis nebo ClassNumberAxisSubRow.</param>
		/// <returns></returns>
		private IDataRow _ReadRowsPlanAxisSOne(GID requestGId)
		{
			// GID osy: Pozor, sem chodí requesty pro dvě čísla tříd:
			// Důvod: v grafu Gantt jsou dva typy řádků: 
			//   a) ClassNumber == Constants.ClassNumberAxis       : hlavní řádek, bez rozpadu na operace,
			//   b) ClassNumber == Constants.ClassNumberAxisSubRow : podřízený řádek, rozpady na operace, na dílce.
			// V systému ale najdeme data osy S jen pod číslem třídy hlavní!
			GID axisGId = requestGId;
			if (axisGId.ClassNumber == Constants.ClassNumberAxisSubRow)
				axisGId = new GID(Constants.ClassNumberAxis, requestGId.RecordNumber);

			// Najdu si vrcholové záznamy osy S = tj. ty, které jsou příjemkou nejvyššího finálu, do kterého míří požadovaný záznam osy S:
            List<GID> finalGIdList = this.AxisHeap.FindRelationLastIDData(axisGId, relItem => relItem.ItemGIDPrev.ClassNumber == Constants.ClassNumberAxis);    // delegate(PlanningItemRelationCls relItem) { return (relItem.ItemGIDPrev.ClassNumber == Constants.ClassNumberAxis); });

			// Pro každý finální řádek si načtu kompletní strom (Main row a Sub rows):
			foreach (GID finalGId in finalGIdList)
			{
                IEnumerable<IDataRow> axisTree = _ReadRowsGetPlanAxisSOneList(finalGId, null);

				// Pokusím se v soupisu najít řádek, který odpovídá vstupnímu GIDu:
				foreach (IDataRow axisRow in axisTree)
				{
					if (axisRow.GId == requestGId)
						return axisRow;
				}
			}
			return null;
		}
		/// <summary>
		/// Metoda sestaví a vrátí soupis řádků, které představují podřízené pracovní úkoly 
		/// k cílovému záznamu osy S, který je výdejem (do poptávky, do komponenty).
		/// Metoda dostává klíč cílového záznamu osy, datum tohoto výdeje.
		/// Přes referenci plní datový interval (plní počátek pracovního výkonu) 
		/// a přidává položky jednotlivých vizuálních řádků do souhrnu podřízených řádků.
		/// </summary>
		/// <remarks>
		/// Metoda nejprve dohledává skladové převody, které předcházejí danému výdeji, a z nich střádá čas těchto skladových přesunů.
		/// Současně vyhledává tasky, které do těchto skladových přesunů vstupují = výroba výsledného dílce (může jich být více).
		/// Pro každý tento finální task zakládá nový řádek do soupisu řádků.
		/// Pro každý tento finální task dohledává jeho předchozí vztahy, a pokud jde o TaskInner, pak zakládá další elementy do téhož řádku.
		/// Pokud jde o vztah Výdej komponenty (AxisToTask), pak rekurzivně spouští tuto metodu = dohledávám časy převodu, a zakládám řádky pro podřízené dílce.
		/// </remarks>
		/// <param name="axisGId">Cílový záznam osy S, který je výdejem (výsledek do poptávky, anebo komponenta do nadřízené výroby). Tento GID slouží jako klíč pro hledání podřízených.</param>
        /// <param name="loadMaterial">Načítat i řádky za materiál jako oddělené sub-rows pod řádkem dílce?</param>
		/// <param name="parentTime">Průběžně střádaný časový interval pro parent řádek</param>
		/// <param name="subRowList">Seznam podřízených řádků</param>
		private void _RowAxisSAddSubTasks(GID axisGId, Func<MaterialPlanAxisItemCls, bool> filter, bool loadMaterial, ref VisualTaskTimeCls parentTime, ref List<IDataRow> subRowList)
		{
			// Na vstupu není záznam materiálové osy: odmítneme jej:
			if (axisGId.ClassNumber != MaterialPlanAxisItemCls.ClassNr) return;

			// Antiduplicitní tabulky:
			Hashtable axisGIdHt = new Hashtable();
			Hashtable scanGIdHt = new Hashtable();
			Hashtable compGIdHt = new Hashtable();

			// Které GIDy (výdejka z osy S) budu zpracovávat?  Vložit první výdejku (výdej do poptávky):
			Queue<GID> nextReq = new Queue<GID>();
			nextReq.Enqueue(axisGId);
			while (nextReq.Count > 0)
			{
				// Vyzvedneme si ze seznamu práce číslo záznamu osy S, pro kterou budeme hledat jeho výrobní příjmy (příjmy z výroby polotovarů):
				GID currentAxisGId = nextReq.Dequeue();
				if (axisGIdHt.ContainsKey(currentAxisGId)) continue;
				axisGIdHt.Add(currentAxisGId, null);

				// Najdu všechny vztahy TaskToAxisFinal + TaskToAxisByProduct = operace, které svojí příjmovou stranou zásobují náš záznam osy S:
				//  (hledám i použitelné vedlejší produkty)
				List<PlanningItemRelationCls> taskAxisRelList = this.AxisHeap.FindTargetPrevTaskRelList(currentAxisGId, true);

				// Nalezené příjemky na osu S představují příjem z výroby (výrobek / vedlejší produkt), 
				//  a je pro ně třeba založit nové řádky v grafu, které popisují výrobu daného dílce:
				foreach (PlanningItemRelationCls taskAxisRel in taskAxisRelList)
				{
					// Duplicita čísla záznamu osy S pro příjemku => skip:
					if (scanGIdHt.ContainsKey(taskAxisRel.ItemGIDNext)) continue;
					scanGIdHt.Add(taskAxisRel.ItemGIDNext, null);

					// Pro daný záznam o příjmu dílce z výroby na sklad založí řádek do grafu, vloží do něj elementy za jednotlivé operace,
					// najde komponenty (záznamy osy S o výdeji do komponent) a vloží je do ref seznamu axisSubLst:
					List<GID> axisSubLst = new List<GID>();
                    this._RowAxisSAddSubTaskOne(axisGId, taskAxisRel, filter, loadMaterial, ref parentTime, ref subRowList, ref axisSubLst);

                    // Výdeje komponent (v poli axisSubLst) do tohoto dílce si vložím do zásobníku výdejek pro prozkoumání v příštím kole:
					foreach (GID axisSubRqs in axisSubLst)
					{
						if (compGIdHt.ContainsKey(axisSubRqs)) continue;   // Duplicita => skip
						compGIdHt.Add(axisSubRqs, null);                   // Antiduplicita.
						nextReq.Enqueue(axisSubRqs);                       // Osa S : výdejka do komponenty - její GID do zásobníku výdejek.
					}
					// Dodělám si příjemky z výroby v jedné úrovni (seznam vztahů relRcpList), 
				}	//  a až je dodělám všechny tak z téhle smyčky vypadnu 
			}		//  a půjdu hledat příjmy pro další výdejky komponent, které jsem si nastřádal do zásobníku nextReq.
		}
		/// <summary>
		/// Najde rozpad dílce na výrobní operace, pro dílec založí řádek do grafu osy S, jednotlivé operace do něj vloží jako elementy,
		/// a po cestě najde výdejky komponent do této výroby.
		/// </summary>
		/// <param name="axisGId">main záznam osy S, jeho GID. Tento GID se vkládá do podřízených řádků grafu (typu PlanningVisualDataRowCls) jako ParentGId.</param>
		/// <param name="taskAxisRel">Záznam o vztahu o příjmu z operace výroby dílce na osu S (operace může být poslední anebo nemusí, podle toho zda jde o výrobek nebo vedlejší produkt)</param>
        /// <param name="loadMaterial">Načítat i řádky za materiál jako oddělené sub-rows pod řádkem dílce?</param>
        /// <param name="parentTime">Čas nadřízeného záznamu, do něj se přidávají časy z podřízených operací</param>
		/// <param name="subRowList">Ref soupis řádků grafu, sem jeden řádek přidám.</param>
		/// <param name="axisSubLst">Ref/Out soupis výdejek do komponent pro tento jeden rozpad. Může obsahovat duplicity, vyšší úroveň si je ohlídá.</param>
		private void _RowAxisSAddSubTaskOne(GID axisGId, PlanningItemRelationCls taskAxisRel, Func<MaterialPlanAxisItemCls, bool> filter, bool loadMaterial, ref VisualTaskTimeCls parentTime, ref List<IDataRow> subRowList, ref List<GID> axisSubLst)
		{
			// 1. Založím řádek pro graf za příjemku na osu S, která představuje příjem z výroby daného dílce:
			MaterialPlanAxisItemCls axisTask = this._FindItemAxisS(taskAxisRel.ItemGIDNext);     // Podle GIDu osy S najdu záznam osy o příjmu z výroby
			if (axisTask == null) return;
			PlanningVisualDataRowCls rowTask = this.PlanningData.VisualGetDataRowForAxisSGanttSub(axisTask);    // Ze záznamu osy S vytvořím řádek grafu
			if (rowTask == null) return;            // Z nějakých důvodů jsem nevytvořil řádek.
			rowTask.ParentGId = axisGId;            // Signalizovat parent GID pro tento podřízený řádek: podřízený = výroba dílce, parent = finál (poptávka, výrobní příkaz: Output řádek)
            bool axisTaskAdd = _AxisSComplyFilter(axisTask, filter);                             // Záznam axisTask vyhovuje filtru

			// 2. Najít sadu záznamů (nejdříve GIDy), které tvoří posloupnost operací, počínaje operací, která jde do příjemky:
			List<PlanningItemRelationCls> componentList = new List<PlanningItemRelationCls>();   // Budeme chtít najít i komponenty operací.
			List<GID> taskList = this.AxisHeap.FindTaskGIdInnerList(taskAxisRel.ItemGIDPrev, TimeRange.TimeDirection.ToHistory, ref componentList);

			// 3. Pro každou operaci založím element = najdu task a podle něj zavedu element:
			bool containVisibleElement = false;
			foreach (GID taskGId in taskList)
				this._RowAxisSAddSubElement(rowTask, taskGId, ref parentTime, ref containVisibleElement);
		
            // 4. Projdu komponenty vstupující do dílce (do operací), a zpracuji je: 
            //   a) materiálové komponenty vytvoří sub-rows s elementy = jednotlivé dodací lhůty, 
            //   b) vyráběné komponenty (podsestavy) předám do pole axisSubLst, a ve vyšší proceduře se zmapují jako další dílce vyráběné pro aktuální technologii.
            List<IDataRow> compRowList = new List<IDataRow>();
            foreach (PlanningItemRelationCls compRelItem in componentList)
                this._RowAxisSAddSubStructure(rowTask, compRelItem, filter, loadMaterial, ref compRowList, ref axisSubLst, ref containVisibleElement);

			// 5. Pokud řádek obsahuje alespoň jeden element s kladnou délkou anebo jeden podřízený řádek, dokončím řádek rowTask a přidám ho do výsledného soupisu řádků do grafu:
			if (containVisibleElement || compRowList.Count > 0)
			{
                if (axisTaskAdd)
                {
                    rowTask.AllElementsLoaded = true;   // Všechny elementy jsou načteny (tedy, budou za chvilku)
                    subRowList.Add(rowTask);            // Řádek do výstupu, odkud se převezme do grafu
                }
                subRowList.AddRange(compRowList);       // Řádky komponent musí být až po řádku dílce, jinak je vizuální vrstva nezařadí do grafu (odvolávají se na parenta, který musí být před nimi).
			}
		}
        /// <summary>
        /// Zpracuje výdej jedné komponenty do operace.
        /// Zjistí, zda jde o objednávaný materiál nebo vyráběný dílec, a podle toho se zachová.
        /// </summary>
        /// <param name="rowTask">Vizuální řádek výroby vyššího dílce, do něhož se vydává aktuální komponenta.</param>
        /// <param name="compRelItem">Vztah o výdeji komponenty</param>
        /// <param name="loadMaterial">Načítat i řádky za materiál jako oddělené sub-rows pod řádkem dílce? Pokud ano, pak se založí (nebo najde) vizuální řádek pro PJ = komponenta, a do něj se vloží elementy = dodací časy</param>
        /// <param name="compRowList">Soupis vizuálních řádků = výroba všech dílců + dodávky komponent pro jeden Output řádek (poptávka / VP na sklad)</param>
        /// <param name="axisSubLst">Soupis klíčů GID = záznamy osy S o příjmu podřízeného dílce z výroby na sklad (=příjem na osu S z poslední operace), tyto podřízené dílce se budou mapovat ve vyšší metodě jako další vizuální výrobní řádek.</param>
        /// <param name="containVisibleElement">ref příznak, že na řádku rowTask existuje cokoliv, proč by se měl zobrazovat (má elementy anebo do něj vstupují materiály, které se zobrazují)</param>
        private void _RowAxisSAddSubStructure(PlanningVisualDataRowCls rowTask, PlanningItemRelationCls compRelItem, Func<MaterialPlanAxisItemCls, bool> filter, bool loadMaterial, ref List<IDataRow> compRowList, ref List<GID> axisSubLst, ref bool containVisibleElement)
        {
            // Co budu hledat: všechny záznamy na ose S doleva (Prev), které reprezentují balancování na ose + předávky mezi sklady (návrh na příjem + návrh na výdej)
            // Kde hledání skončí: 
            //   a) když najdu vztah z tasku na osu (=příjem finálu), předám ho do axisSubLst
            //   b) když najdu (vlevo = Prev) záznam osy S typu Návrh na objednání, nebo výdej ze skladové zásoby
            // Co budu zobrazovat: všechny záznamy osy S, které mají čas větší než 0 anebo všechny, pokud je povoleno v "enableAll". Pouze tehdy, když je povoleno parametrem loadMaterial.
            // Jak zobrazovat: vytvořím řádek pro záznam osy "Výdej komponenty do operace" (pro klíč compRelItem.ItemGIDPrev), a do něj budu vkládat elementy = časy výdeje.
            //  Jeden běh této metody = 0 nebo 1 řádek. 
            //  Zatím nebudu řádky "slučovat" (více komponent stejné PJ do stejné operace do jednoho řádku = výdeje dle transportních dávek). Pak by klíčem řádku nemohl být výdej do komponenty, ale task * plánovací jednotka?
            //  Text řádku = plánovací jednotka. Text elementu = pohyb, sklad, množství.

            // Vizuální řádek vytvořím, až pokud toho bude třeba:
            PlanningVisualDataRowCls compRow = null;
            GID compGId = compRelItem.ItemGIDPrev;
            bool enableAll = true;

            // Které GIDy (záznamy osy S) budu zpracovávat?  Vložit první záznam (strana Prev = výdejku ze skladu do komponenty):
            Hashtable axisGIdHt = new Hashtable();
			Queue<GID> nextReq = new Queue<GID>();
            nextReq.Enqueue(compGId);
			while (nextReq.Count > 0)
			{
				// Vyzvedneme si ze seznamu práce číslo záznamu osy S, pro kterou budeme hledat její zdroje (výdeje ze skladu, balance, transfery, objednávky):
				GID axisGId = nextReq.Dequeue();
				if (axisGIdHt.ContainsKey(axisGId)) continue;
				axisGIdHt.Add(axisGId, null);

                MaterialPlanAxisItemCls axis = this._FindItemAxisS(axisGId);
                if (axis == null) continue;
                bool axisComplyFilter = _AxisSComplyFilter(axis, filter);

                // Pokud záznam osy je o příjmu z výroby, tak jej dáme do seznamu axisSubLst = zmapuje se jako dílec ve vyšší vrstvě:
                if (axis.ChangeType == PlanSAxisChangeType.IncrementByPlanProductOrder ||
                    axis.ChangeType == PlanSAxisChangeType.IncrementByRealProductOrder ||
                    axis.ChangeType == PlanSAxisChangeType.IncrementByPlanByProductSuitable ||
                    axis.ChangeType == PlanSAxisChangeType.IncrementByPlanByProductDissonant ||
                    axis.ChangeType == PlanSAxisChangeType.IncrementByRealByProductSuitable ||
                    axis.ChangeType == PlanSAxisChangeType.IncrementByRealByProductDissonant ||
                    axis.ChangeType == PlanSAxisChangeType.IncrementByOrderProduction)
                {
                    axisSubLst.Add(axisGId);
                }
                // Pokud jde o snížení nebo zvýšení zásoby plánem nebo reálným skladovým pohybem nebo objednávkou k dodavateli, a časový interval je kladný, tak pro tento časový interval přidáme element:
                else if (axis.ChangeType == PlanSAxisChangeType.DecrementByPlanComponent ||
                         axis.ChangeType == PlanSAxisChangeType.DecrementByPlanEnquiry ||
                         axis.ChangeType == PlanSAxisChangeType.DecrementByProposalRequisition ||
                         axis.ChangeType == PlanSAxisChangeType.DecrementByRealComponent ||
                         axis.ChangeType == PlanSAxisChangeType.DecrementByRealEnquiry ||
                         axis.ChangeType == PlanSAxisChangeType.IncrementByPlanSupplierOrder ||
                         axis.ChangeType == PlanSAxisChangeType.IncrementByProposalReceipt ||
                         axis.ChangeType == PlanSAxisChangeType.IncrementByRealSupplierOrder ||
                         axis.ChangeType == PlanSAxisChangeType.IncrementByPlanStockTransfer)
                {
                    if (axisComplyFilter && (enableAll || axis.DatePlanTime.Seconds > 0d))
                    {   // Přidat element, pokud osa S vyhovuje filtru, a čas na ose je kladný (nebo jsou povoleny nulové časy):
                        if (compRow == null)
                        {   // Vytvoříme vizuální řádek za komponentu (podřízený pod operací), protože do něj budeme přidávat element vyjadřující převod na ose S:
                            MaterialPlanAxisItemCls compAxis = this._FindItemAxisS(compGId);
                            compRow = this.PlanningData.VisualGetDataRowForAxisSGanttComp(compAxis);    // Ze záznamu osy S vytvořím řádek grafu
                            if (compRow == null) return;            // Z nějakých důvodů jsem nevytvořil řádek.
                            compRow.ParentGId = rowTask.GId;        // Signalizovat parent GID pro tento podřízený řádek: podřízený = komponenta, parent = výroba dílce
                        }
                        if (compRow != null)
                        {
                            this._RowAxisSAddCompElement(compRow, axis, ref containVisibleElement);
                        }
                    }

                    // Najdu z osy S vztahy doleva (směr Prev), a za skladové pohyby přidám jejich klíče Prev do zásobníku práce:
                    List<PlanningItemRelationCls> prevRelList = this.AxisHeap.FindRelationPrevIDData(axisGId, r =>
                        (r.Relation == PlanningItemRelationType.AxisToTask ||
                         r.Relation == PlanningItemRelationType.AxisBalance ||
                         r.Relation == PlanningItemRelationType.AxisTransfer ||
                         r.Relation == PlanningItemRelationType.TaskToAxisByProductDissonant ||
                         r.Relation == PlanningItemRelationType.TaskToAxisByProductSuitable ||
                         r.Relation == PlanningItemRelationType.TaskToAxisFinal));
                    foreach (PlanningItemRelationCls relItem in prevRelList)
                    {
                        if (relItem.ItemGIDPrev.RecordNumber != 0)
                            nextReq.Enqueue(relItem.ItemGIDPrev);
                    }
                }
            }
            // Pokud jsme vytvořili řádek pro tuto komponentu, dáme jej do výstupu:
            if (compRow != null)
            {
                compRowList.Add(compRow);
                rowTask.HasSubNodes = true;
                if (!containVisibleElement)
                    containVisibleElement = true;
            }
        }
		/// <summary>
		/// Do grafického řádku vloží jeden element za jeden task.
		/// Časy tohoto tasku přičte k parentTime
		/// </summary>
		/// <param name="rowTask"></param>
		/// <param name="taskGId"></param>
		/// <param name="parentTime"></param>
		private void _RowAxisSAddSubElement(PlanningVisualDataRowCls rowTask, GID taskGId, ref VisualTaskTimeCls parentTime, ref bool containVisibleElement)
		{
			CapacityPlanWorkItemCls task = this._FindItemTaskC(taskGId);
			if (task == null) return;

			// Pokud potřebná kapacita tasku je nula, pak task nebudu dávat k zobrazení (on by stejně nebyl vidět, šlo by o element s nulovou délkou):
			if (task.CapacityRequiredTotal <= 0M) return;

			// Přidám kapacitu a časy z tasku do parent času:
			parentTime.AddTimeFromTask(task);

			// Vytvořím element:
			PlanningVisualDataElementCls element = new PlanningVisualDataElementCls(task.TaskGID, rowTask.GId, PlanUnitCTaskCls.ClassNr, GID.Empty, task.TimeWork, DataElementEditingMode.DeadElement);
			DataElementEditingMode editMode = DataElementEditingMode.Selectable;
			if (task.IsFixedTask)
				editMode |= DataElementEditingMode.PinnedItem;
			element.SetElementProperty(GraphElementLayerType.ItemLayer, editMode, GraphElementVisualType.Type1, GraphElementShapeType.Hexagon);
            element.BackColor = this._VisualGetColorForTask(task, PlanningDataCls.ColorSet.GanttOperationPlan, PlanningDataCls.ColorSet.GanttOperationProductPrepared, PlanningDataCls.ColorSet.GanttOperationProductPartial);

			// Můj čas úkolu (délka, složení fází TMC-TBC-TAC-TEC):
			VisualTaskTimeCls thisTime = VisualTaskTimeCls.CreateFromEnd(task.TimeWork.End);
			thisTime.AddTimeFromTask(task);
			this._ElementSetFragment(element, thisTime);

			this.PlanningData.VisualSetElementTextForTaskCGantt(task, element);

			// Neprázdný element vložit do řádku:
			if (element.TimeRange.Seconds > 0D)
			{
				rowTask.ElementList.Add(element);
				// Příznak, že tento sub row má nějaký nenulový element:
				if (!containVisibleElement)
					containVisibleElement = true;
			}
		}
        /// <summary>
        /// Do grafického řádku vloží jeden element za převod komponenty v rámci skladů.
        /// </summary>
        /// <param name="compRow">Vizuální řádek = výdej do komponenty</param>
        /// <param name="axis">Záznam osy S = pohyb určitého množství komponenty po skladech</param>
        /// <param name="containVisibleElement"></param>
        private void _RowAxisSAddCompElement(PlanningVisualDataRowCls compRow, MaterialPlanAxisItemCls axis, ref bool containVisibleElement)
        {
            if (axis == null)
                return;

            // Vytvořím element:
            PlanningVisualDataElementCls element = new PlanningVisualDataElementCls(axis.AxisGID, compRow.GId, PlanUnitSAxisCls.ClassNr, GID.Empty, axis.DatePlanTime, DataElementEditingMode.DeadElement);
            DataElementEditingMode editMode = DataElementEditingMode.Selectable;
            element.SetElementProperty(GraphElementLayerType.ItemLayer, editMode, GraphElementVisualType.Type1, GraphElementShapeType.Hexagon);
            element.SetColor(this._VisualGetColorForAxisComp(axis));
            element.CaptionVisibility = CaptionVisibilityMode.Always;
            element.SuffixKeyForToolTip = "Comp";

            this.PlanningData.VisualSetElementTextForAxisSGanttComp(axis, element);

            compRow.ElementList.Add(element);
            if (!containVisibleElement)
                containVisibleElement = true;
        }
        /// <summary>
        /// Vrátí true, pokud daný záznam osy S (MaterialPlanAxisItemCls) vyhovuje danému filtru.
        /// Pokud filtr není zadán, vrací true (= není třeba filtrovat data).
        /// </summary>
        /// <param name="axis"></param>
        /// <param name="filter"></param>
        /// <returns></returns>
        private static bool _AxisSComplyFilter(MaterialPlanAxisItemCls axis, Func<MaterialPlanAxisItemCls, bool> filter)
        {
            return (filter == null || filter(axis));
        }
        #endregion
		#region Generátory elementů do řádků za osu S (tvar, barevnost, textové popisky)
		/// <summary>
		/// Do vizuálního řádku přidá element za daný záznam osy S a daný čas parentTime.
		/// </summary>
		/// <param name="visualRow">Vizuální řádek</param>
		/// <param name="axisGId">GID záznamu osy S</param>
		/// <param name="parentTime">Souhrn časů z podřízených tasků</param>
		private void _RowAxisSAddElementMain(PlanningVisualDataRowCls visualRow, GID axisGId, VisualTaskTimeCls parentTime)
		{
			this._RowAxisSAddElementMain(visualRow, this._FindItemAxisS(axisGId), parentTime);
		}
		/// <summary>
		/// Do vizuálního řádku přidá element za daný záznam osy S a daný čas parentTime.
		/// </summary>
		/// <param name="visualRow">Vizuální řádek</param>
		/// <param name="axis">Data osy S</param>
		/// <param name="parentTime">Souhrn časů z podřízených tasků</param>
		private void _RowAxisSAddElementMain(PlanningVisualDataRowCls visualRow, MaterialPlanAxisItemCls axis, VisualTaskTimeCls parentTime)
		{
			if (axis == null) return;
			PlanningVisualDataElementCls element = _RowAxisSGetElementMain(axis, parentTime);
			visualRow.ElementList.Add(element);
		}
		/// <summary>
		/// Vytvoří a vrátí element pro osu S.
		/// </summary>
		/// <param name="axisGId">GID záznamu osy S</param>
		/// <param name="parentTime">Souhrn časů z podřízených tasků</param>
		/// <returns></returns>
		private PlanningVisualDataElementCls _RowAxisSGetElementMain(GID axisGId, VisualTaskTimeCls parentTime)
		{
			return this._RowAxisSGetElementMain(this._FindItemAxisS(axisGId), parentTime);
		}
		/// <summary>
		/// Vytvoří a vrátí element pro osu S.
		/// </summary>
		/// <param name="axis">Data osy S</param>
		/// <param name="parentTime">Souhrn časů z podřízených tasků</param>
		/// <returns></returns>
		private PlanningVisualDataElementCls _RowAxisSGetElementMain(MaterialPlanAxisItemCls axis, VisualTaskTimeCls parentTime)
		{
			PlanningVisualDataElementCls element = new PlanningVisualDataElementCls(axis.AxisGID, axis.AxisGID, PlanUnitSAxisCls.ClassNr, GID.Empty, parentTime.TimeReal, DataElementEditingMode.Editable);
			element.SetElementProperty(GraphElementLayerType.ItemLayer, DataElementEditingMode.Selectable, GraphElementVisualType.Type1, GraphElementShapeType.Rectangle); // Hexagon);
			Color elementColor = _GetColorElementGanttAxisSMain(axis);
			element.SetColor(elementColor);
			this._ElementSetFragment(element, parentTime);
			this.PlanningData.VisualSetElementTextForAxisSGanttMain(axis, element);
			return element;
		}
		/// <summary>
		/// Do vizuálního řádku přidá element za daný task.
		/// </summary>
		/// <param name="visualRow">Vizuální řádek</param>
		/// <param name="taskGId">GID tasku</param>
		/// <param name="timeRange"></param>
		private void _RowAxisSAddElementTask(PlanningVisualDataRowCls visualRow, GID taskGId, ref VisualTaskTimeCls parentTime)
		{
			this._RowAxisSAddElementTask(visualRow, this._FindItemTaskC(taskGId), ref parentTime);
		}
		/// <summary>
		/// Do vizuálního řádku přidá element za daný task.
		/// </summary>
		/// <param name="visualRow">Vizuální řádek</param>
		/// <param name="task">Data tasku</param>
		/// <param name="timeRange"></param>
		private void _RowAxisSAddElementTask(PlanningVisualDataRowCls visualRow, CapacityPlanWorkItemCls task, ref VisualTaskTimeCls parentTime)
		{
			if (task == null) return;

			parentTime.AddTimeFromTask(task);

			// Můj čas úkolu:
			VisualTaskTimeCls thisTime = VisualTaskTimeCls.CreateFromEnd(task.TimeWork.End);
			thisTime.AddTimeFromTask(task);

			PlanningVisualDataElementCls elementTask = new PlanningVisualDataElementCls(task.TaskGID, visualRow.GId, PlanUnitCTaskCls.ClassNr, GID.Empty, task.TimeWork, DataElementEditingMode.DeadElement);
			elementTask.SetElementProperty(GraphElementLayerType.ItemLayer, DataElementEditingMode.Editable, GraphElementVisualType.Type1, GraphElementShapeType.Hexagon);

            elementTask.BackColor = this._VisualGetColorForTask(task, PlanningDataCls.ColorSet.GanttOperationPlan, PlanningDataCls.ColorSet.GanttOperationProductPrepared, PlanningDataCls.ColorSet.GanttOperationProductPartial);

			this._ElementSetFragment(elementTask, thisTime);
			this.PlanningData.VisualSetElementTextForTaskCGantt(task, elementTask);
			visualRow.ElementList.Add(elementTask);
		}
		/// <summary>
		/// Do elementu vepíše časové fragmenty z dodaného časosběrného objektu
		/// </summary>
		/// <param name="element"></param>
		/// <param name="taskTime"></param>
		private void _ElementSetFragment(PlanningVisualDataElementCls element, VisualTaskTimeCls taskTime)
		{
			element.Fragments = new float[4];
			element.Fragments[0] = taskTime.SecondsTMC;
			element.Fragments[1] = taskTime.SecondsTBC;
			element.Fragments[2] = taskTime.SecondsTAC;
			element.Fragments[3] = taskTime.SecondsTEC;
		}
		#endregion
		#endregion
		#region PODPORA BAREV - určení barvy na základě údajů v datech
		/// <summary>
		/// Vrátí barvu pro element Gantt osy S řádek Main
		/// </summary>
		/// <param name="axis"></param>
		/// <returns></returns>
		private Color _GetColorElementGanttAxisSMain(MaterialPlanAxisItemCls axis)
		{
			switch (axis.ChangeType)
			{
				case PlanSAxisChangeType.DecrementByRealEnquiry:
                    return this.PlanningData.PlanningParameters.Colors.ElementGanttAxisSMainEnqReal;
				case PlanSAxisChangeType.DecrementByPlanEnquiry:
                    return this.PlanningData.PlanningParameters.Colors.ElementGanttAxisSMainEnqPlan;
				case PlanSAxisChangeType.IncrementByRealProductOrder:
					if (axis.IsForOriginReason(PlanSAxisOriginReason.MinimumStock))
                        return this.PlanningData.PlanningParameters.Colors.ElementGanttAxisSMainProdMin;
					else
                        return this.PlanningData.PlanningParameters.Colors.ElementGanttAxisSMainProdReal;

				case PlanSAxisChangeType.IncrementByPlanProductOrder:
					if (axis.IsForOriginReason(PlanSAxisOriginReason.MinimumStock))
                        return this.PlanningData.PlanningParameters.Colors.ElementGanttAxisSMainProdMin;
					else
                        return this.PlanningData.PlanningParameters.Colors.ElementGanttAxisSMainProdPlan;
			}
            return this.PlanningData.PlanningParameters.Colors.ElementGanttAxisSMainOther;
		}
		#endregion
		#region PODPORA - HLEDÁNÍ DAT OSY S A TASKU PODLE GIDU

		/// <summary>
		/// Rozšíří předaný časový interval o čas daného záznamu osy S.
		/// Rozšiřuje pouze segment TimeTMS = čas skladového přesunu.
		/// </summary>
		/// <param name="timeRange"></param>
		/// <param name="axisS"></param>
		private void _RowAxisSEnlargeTime(ref VisualTaskTimeCls parentTime, GID axisS)
		{
			//MaterialPlanAxisItemCls axis = this._RowAxisSFindItem(axisS);
			//if (axis != null)
			//    parentTime.ExpandTimeStock(axis.DatePlanTime);
		}
		/// <summary>
		/// Najde a vrátí záznam osy S pro daný GID. Předem kontroluje NotEmpty a číslo třídy GIDu.
		/// Pokud neodpovídá GID nebo třída, anebo záznam není nalezen, vrací null. Nevyhodí chybu.
		/// </summary>
		/// <param name="axisS"></param>
		/// <returns></returns>
		private MaterialPlanAxisItemCls _FindItemAxisS(GID axisS)
		{
			if (axisS.NotEmpty && axisS.ClassNumber == MaterialPlanAxisItemCls.ClassNr)
				return this.AxisHeap.FindAxisSItem(axisS.RecordNumber);
			return null;
		}
		/// <summary>
		/// Najde a vrátí záznam kapacitního tasku pro daný GID. Předem kontroluje NotEmpty a číslo třídy GIDu.
		/// Pokud neodpovídá GID nebo třída, anebo záznam není nalezen, vrací null. Nevyhodí chybu.
		/// </summary>
		/// <param name="taskC"></param>
		/// <returns></returns>
		private CapacityPlanWorkItemCls _FindItemTaskC(GID taskC)
		{
			if (taskC.NotEmpty && taskC.ClassNumber == CapacityPlanWorkItemCls.ClassNr)
				return this.AxisHeap.FindTaskCItem(taskC.RecordNumber);
			return null;
		}
		#endregion
		#region ČTENÍ DAT Z PROCESU DO GUI - ELEMENTY (POLOŽKY GRAFŮ)
		/// <summary>
		/// Načtení elementů pro daný řádek a období
		/// </summary>
		/// <param name="requestInput">Vstupující požadavek</param>
		private void _ReadElements(DataSourceRequest requestInput)
		{
			if (this.PlanningData.CurrentStateCanGetData)
			{
				DataSourceRequestReadElements request = DataSourceRequest.TryGetTypedRequest<DataSourceRequestReadElements>(requestInput);
				switch (request.RequestRowGId.ClassNumber)
				{
					case Constants.ClassNumberAxis:
					case Constants.ClassNumberAxisSubRow:
						this._ReadElementsAxisS(request);
						break;
					case PlanUnitCCls.ClassNr:
						this._ReadElementsPlanUnitC(request);
						break;
					default:
						Throw.SysError(MessageInfo.Get("Požadavek na načítání elementů třídy %0, která načítání elementů nepodporuje.", request.RequestRowGId.ClassNumber));
						break;
				}
			}
		}
		/// <summary>
		/// Načtení zcela konkrétních elementů, jsou zadané jejich pozicí (plánovací jednotka, WorkUnit)
		/// </summary>
		/// <param name="requestInput">Vstupující požadavek</param>
		private void _ReadElementsMap(DataSourceRequest requestInput)
		{
			if (this.PlanningData.CurrentStateCanGetData)
			{
				DataSourceRequestReadElements request = DataSourceRequest.TryGetTypedRequest<DataSourceRequestReadElements>(requestInput);
				List<IDataElement> result = new List<IDataElement>();
				foreach (DataPointerStr dataPointer in request.RequestElementsMap)
				{
					switch (dataPointer.Row.ClassNumber)
					{
						case PlanUnitCCls.ClassNr:
							this._ReadElementsOnePlanUnitC(dataPointer, ref result);
							break;
					}
				}
				request.ResultElements = result;
			}
		}
		/// <summary>
		/// Zajistí načtení elementů pro daný záznam osy S (request.RequestRowGId). Vždy jen pro jeden řádek, vždy bude jen jeden element.
		/// Běžně se načítají současně s řádkem osy S, ale po interaktivní změně a invalidaci dat řádku je třeba je načíst znovu.
		/// </summary>
		/// <param name="request"></param>
		private void _ReadElementsAxisS(DataSourceRequestReadElements request)
		{
			// GID osy: Pozor, sem chodí requesty pro dvě čísla tříd:
			// Důvod: v grafu Gantt jsou dva typy řádků: 
			//   a) ClassNumber == Constants.ClassNumberAxis       : hlavní řádek, bez rozpadu na operace,
			//   b) ClassNumber == Constants.ClassNumberAxisSubRow : podřízený řádek, rozpady na operace, na dílce.
			// V systému ale najdeme data osy S jen pod číslem třídy hlavní!
			GID axisGId = request.RequestRowGId;
			// GID ponechám beze změn, protože potřebuji najít data právě pro ten GID (včetně čísla třídy), a ne pro hlavní řádek.
			// Následující metoda this._ReadRowsPlanAxisSOne() si s dvěma čísly tříd osy S poradí sama.
			
			// Pokusím se najít data hledaného řádku (poměrně komplikovaná metoda, které k GID řádku dohledá jeho finální řádek,
			//  pro ten finální řádek sestaví plný rozpad, a z něj najde a vrátí hledaná řádek - to vše kvůli návaznostem):
			IDataRow axisRow = this._ReadRowsPlanAxisSOne(axisGId);
			if (axisRow == null) return;

			// Vložím elementy z nalezeného řádku do zdejšího requestu:
			request.ResultElements.AddRange(axisRow.ElementList);
			request.ResultLoadAllElements = true;
		}
		/// <summary>
		/// Načtení elementů pro daný řádek a období, pro řádek třídy Kapacitní plánovací jednotka
		/// </summary>
		/// <param name="request"></param>
		private void _ReadElementsPlanUnitC(DataSourceRequestReadElements request)
		{
			switch (request.RequestedGraphMode)
			{
				case RowGraphMode.CapacityUtilization:
					this._ReadElementsPlanUnitCGraphCU(request);
					break;
				case RowGraphMode.TaskCapacityLink:
					this._ReadElementsPlanUnitCGraphTL(request);
					break;
			}
		}
		/// <summary>
		/// Metoda načte jeden element grafu a přidá jej do seznamu.
		/// </summary>
		/// <param name="dataPointer"></param>
		/// <param name="result"></param>
		private void _ReadElementsOnePlanUnitC(DataPointerStr dataPointer, ref List<IDataElement> result)
		{
			if (dataPointer.Element.ClassNumber == Constants.ClassNumberWork)
			{
				WorkUnitCls workUnit = this.AxisHeap.FindIWorkItem(dataPointer.Element.RecordNumber);
				if (workUnit != null)
				{
					PlanningVisualDataElementCls element = this._ReadElementsCreateTaskLinkFromWorkUnit(workUnit);
					result.Add(element);
				}
			}
		}
		/// <summary>
		/// Načtení dat elementů pro kapacitní jednotku a graf Využití kapacit
		/// </summary>
		/// <param name="request"></param>
		private void _ReadElementsPlanUnitCGraphCU(DataSourceRequestReadElements request)
		{
			CapacityUnitCls unit = this.CapacityData.GetCapacityUnit(request.RequestRowGId.RecordNumber);
			if (unit == null) return;
			List<CapacityLevelItemCls> levelList = unit.GetCapacityLevels(request.RequestedTimeRange);
			foreach (CapacityLevelItemCls level in levelList)
			{
				float utilizationRatio = (float)(level.CapacityTotal > 0M ? level.CapacityUsed / level.CapacityTotal : 0M);
				PlanningVisualDataElementCls element = new PlanningVisualDataElementCls(new GID(PlanUnitCClCls.ClassNr, level.LevelID), request.RequestRowGId, PlanUnitCClCls.ClassNr, GID.Empty, level.Time);
				element.SetElementProperty(GraphElementLayerType.ItemLayer, DataElementEditingMode.DeadElement, GraphElementVisualType.Type1, 1F, utilizationRatio, 1F);
				element.SetElementProperty(GraphElementShapeType.CapacityUtilization);
				element.SetColor(this._VisualGetColorForCapacityLevel(level));
				request.ResultElements.Add(element);
			}
		}
		/// <summary>
		/// Načtení dat elementů pro kapacitní jednotku a graf Vztah tasku a kapacitní jednotky
		/// </summary>
		/// <param name="request"></param>
		private void _ReadElementsPlanUnitCGraphTL(DataSourceRequestReadElements request)
		{
			CapacityUnitCls unit = this.CapacityData.GetCapacityUnit(request.RequestRowGId.RecordNumber);
			if (unit == null) return;
			List<CapacityLevelItemCls> levelList = unit.GetCapacityLevels(request.RequestedTimeRange);
			foreach (CapacityLevelItemCls level in levelList)
			{
				// 1. Element za stav kapacit:
				PlanningVisualDataElementCls elementCap = new PlanningVisualDataElementCls(new GID(PlanUnitCClCls.ClassNr, level.LevelID), request.RequestRowGId, PlanUnitCClCls.ClassNr, GID.Empty, level.Time);
				elementCap.SetElementProperty(GraphElementLayerType.SubLayer, DataElementEditingMode.DeadElement, GraphElementVisualType.Type1);
				elementCap.SetElementProperty(GraphElementShapeType.Rectangle);
				elementCap.SetColor(this._VisualGetColorForCapacityLevel(level));
				request.ResultElements.Add(elementCap);

				// 2. Element za každý jednotlivý Job = TaskLink:
				foreach (CapacityDeskCls desk in level.CapacityDesk)
				{
					foreach (CapacityJobItemCls job in desk.JobList)
					{
						WorkUnitCls workUnit = (WorkUnitCls)job.WorkUnit;
						PlanningVisualDataElementCls elementLink = _ReadElementsCreateTaskLinkFromWorkUnit(workUnit);
						request.ResultElements.Add(elementLink);
					}
				}
			}
		}
		/// <summary>
		/// Vygeneruje a vrátí vizuální element
		/// </summary>
		/// <param name="workUnit">Data WorkUnit, které se mají zobrazovat</param>
		/// <returns></returns>
		private PlanningVisualDataElementCls _ReadElementsCreateTaskLinkFromWorkUnit(WorkUnitCls workUnit)
		{
			DataPointerStr dataPointer = workUnit.DataPointer;
			PlanningVisualDataElementCls elementLink = new PlanningVisualDataElementCls(
				dataPointer.Element,
				dataPointer.Row,
                PlanUnitCUnitCls.ClassNr,
			    new GID(Constants.ClassNumberTask, workUnit.TaskID), 
				workUnit.WorkTime, 
				DataElementEditingMode.Editable);
			DataElementEditingMode editMode = DataElementEditingMode.Movable | DataElementEditingMode.TransponableToAnotherRow | DataElementEditingMode.ShiftItemsOnSameThread | DataElementEditingMode.AffinityToInitialTime;
			if (workUnit.IsFixedTask)
				editMode |= DataElementEditingMode.BlockingItem; // DataElementEditingMode.PinnedItem;
			elementLink.SetElementProperty(GraphElementLayerType.ItemLayer, editMode, GraphElementVisualType.Type2, GraphElementShapeType.Rectangle);

			elementLink.UseRatio = (float)workUnit.UseRatio;

			elementLink.LinkGroup = workUnit.TaskData.LinkGroup;
			elementLink.BackColor = this._VisualGetColorForWorkUnit(workUnit);
			
			this.PlanningData.VisualSetElementTextForTaskCLink(workUnit, elementLink);
			return elementLink;
		}
		/// <summary>
		/// Vrátí barvu elementu typu Gantt - operace
		/// </summary>
		/// <param name="task"></param>
		/// <returns></returns>
		private Color _VisualGetColorForAxisSub(CapacityPlanWorkItemCls task)
		{
			Color ganttColor = task.OperationColor;
			if (ganttColor.IsEmpty)
			{
				PlanningDataCls.ColorSet colorSet = 
					(task.DocumentType == TDocumentType.PlanApproved ? PlanningDataCls.ColorSet.GanttOperationPlan :
					(task.DocumentType == TDocumentType.Production && task.QtyRequired < task.QtyFinal ? PlanningDataCls.ColorSet.GanttOperationProductPartial :
					PlanningDataCls.ColorSet.GanttOperationProductPrepared));
				ganttColor = this.PlanningData.VisualGetColor(colorSet, task.AxisID);
			}
			return ganttColor;
		}
        /// <summary>
        /// Vrátí barvu elementu typu Gantt - komponenta
        /// </summary>
        /// <param name="task"></param>
        /// <returns></returns>
        private Color _VisualGetColorForAxisComp(MaterialPlanAxisItemCls axis)
        {
			PlanningDataCls.ColorSet colorSet = 
                ((axis.ChangeType == PlanSAxisChangeType.IncrementByRealSupplierOrder) ? PlanningDataCls.ColorSet.GanttComponentOrderReal : 
                ((axis.ChangeType == PlanSAxisChangeType.IncrementByPlanSupplierOrder) ? PlanningDataCls.ColorSet.GanttComponentOrderPlan : PlanningDataCls.ColorSet.GanttComponentStock));
            return this.PlanningData.VisualGetColor(colorSet, axis.AxisID);
        }
        /// <summary>
		/// Vrátí barvu elementu typu Stav kapacit.
		/// </summary>
		/// <param name="level"></param>
		/// <returns></returns>
		private Color _VisualGetColorForCapacityLevel(CapacityLevelItemCls level)
		{
			Color levelColor = level.WorkShiftColor;
			if (levelColor.IsEmpty) levelColor = this.PlanningData.VisualGetColor(PlanningDataCls.ColorSet.CapacityUtilization, 0); // level.WorkShift);
			return levelColor;
		}
		/// <summary>
		/// Vrátí barvu elementu typu WorkUnit = barva pro daný task
		/// </summary>
		/// <param name="workUnit"></param>
		/// <returns></returns>
		private Color _VisualGetColorForWorkUnit(WorkUnitCls workUnit)
		{
            return this._VisualGetColorForTask(workUnit.TaskData, PlanningDataCls.ColorSet.TaskLinkPlan, PlanningDataCls.ColorSet.TaskLinkProductPrepared, PlanningDataCls.ColorSet.TaskLinkProductPartial);
		}
        /// <summary>
        /// Vrátí konkrétní barvu pro daný task
        /// </summary>
        /// <param name="taskData"></param>
        /// <returns></returns>
        private Color _VisualGetColorForTask(CapacityPlanWorkItemCls taskData, PlanningDataCls.ColorSet setPlan, PlanningDataCls.ColorSet setPrepared, PlanningDataCls.ColorSet setPartial)
        {
            Color workColor = Color.DarkGreen;
            if (!taskData.OperationColor.IsEmpty)
                workColor = taskData.OperationColor;
            else
            {
                PlanningDataCls.ColorSet colorSet = setPlan;
                if (taskData.DocumentType == TDocumentType.Production || taskData.AxisData.DocumentType == TDocumentType.Production)
                    colorSet = (taskData.IsPartial ? setPartial : setPrepared);
                workColor = this.PlanningData.VisualGetColor(colorSet, taskData.AxisID);
            }
            return workColor;
        }
		#endregion
		#region ČTENÍ VZTAHŮ MEZI ELEMENTY PRO ZOBRAZENÍ SÍTĚ VZTAHŮ
		/// <summary>
		/// Metoda najde pro daný element jeho vztahy na sousední elementy 
		/// a sestavený soupis uloží do request.ResultRelations = new List...()
		/// </summary>
		/// <param name="requestInput">Vstupující požadavek</param>
		private void _CreateRelations(DataSourceRequest requestInput)
		{
			if (this.PlanningData.CurrentStateCanGetData)
			{
				DataSourceRequestReadRelations request = DataSourceRequest.TryGetTypedRequest<DataSourceRequestReadRelations>(requestInput);
				int workID = request.RequestElementPointer.Element.RecordNumber;          // Číslo záznamu WorkID
                if (request.RequestElementPointer.Element.ClassNumber == Constants.ClassNumberWork)
                {
                    // Podle levelu vztahů budu načítat:
                    switch (request.RequestRelationLevel)
                    {
                        case RequestRelationLevel.Standard:
                            // Standard: jen vztahy uvnitř jednoho dílce, mimo sousední paralelní průchod, ne do okolních dílců:
                            request.ResultRelations = this.PlanningData.GetWorkUnitRelations(workID, false, true, false);
                            break;
                        case RequestRelationLevel.Extended:
                            // Rozšířené: všechny vztahy na okolní dílce, ale sousední paralelní průchod ne:
                            request.ResultRelations = this.PlanningData.GetWorkUnitRelations(workID, false, true, true);
                            break;
                    }
                }
			}
		}
		/// <summary>
		/// Načte a vrátí seznam linků mezi elementy.
		/// Linky jsou permanentně viditelné vztahy, které se nezhasínají (na rozdíl od Relations, které se rozsvítí po kliknutí na element, a které slouží k editaci).
		/// Linky spojují záznamy, které na sebe nenavazují, ale mají se držet pospolu z důvodu vyššího příkazu 
		/// (uživatel je chce mít poblíž, ačkoliv jde o elementy z navzájem nespojitých tasků).
		/// </summary>
		/// <param name="requestInput"></param>
		private void _CreateLinkList(DataSourceRequest requestInput)
		{
			if (this.PlanningData.CurrentStateCanGetData)
			{
				DataSourceRequestReadRelations request = DataSourceRequest.TryGetTypedRequest<DataSourceRequestReadRelations>(requestInput);
				if (request.DefaultGraphMode == RowGraphMode.TaskCapacityLink)
					request.ResultRelations = this.PlanningData.LinkedElementGetList();
			}
		}
		#endregion
		#region ŘÍZENÍ MEZIGRAFOVÉ AKTIVITY
		/// <summary>
		/// Řeší mezigrafovou aktivitu: určení odpovídající reakce na zadanou akci.
		/// </summary>
		/// <param name="requestInput">Vstupující požadavek</param>
		private void _FindInterGraphTargetData(DataSourceRequest requestInput)
		{
			DataSourceRequestFindInterGraph request = DataSourceRequest.TryGetTypedRequest<DataSourceRequestFindInterGraph>(requestInput);

			// Rozsah aktivity, která vstupuje do této metody (která je řešena requestem DataSourceRequestType.FindInterGraphTargetData)
			// je daný deklarací grafů v jejich deklarátoru.
			switch (request.EventType)
			{
				case GraphEventType.RowEnter:
					this._FindInterGraphRowEnter(request);
					break;
				case GraphEventType.MouseClick:
					this._FindInterGraphMouseClick(request);
					break;
			}
		}
		/// <summary>
		/// Řeší mezigrafovou aktivitu pro událost RowEnter: vyhledá párové elementy z partnerského grafu
		/// </summary>
		/// <param name="request"></param>
		private void _FindInterGraphRowEnter(DataSourceRequestFindInterGraph request)
		{
			// Pokud jde o vztah z gantu, ze záznamu osy S typu SubAxis (Constants.ClassNumberAxisSubRow), 
			//     a Cíl je TaskCapacityLink z elementu na řádku Kapacitní plánovací jednotka,
			//     pak bych mohl zkusit najít odpovídající WorkUnits:
			if (request.SourceGraphMode == RowGraphMode.Gantt && (request.SourceElement.Row.ClassNumber == Constants.ClassNumberAxisSubRow || request.SourceElement.Row.ClassNumber == Constants.ClassNumberAxis) &&
				request.DefaultGraphMode == RowGraphMode.TaskCapacityLink && request.GraphDeclaration.RowClassNumber == PlanUnitCCls.ClassNr)
			{
				this._FindInterGraphWorkForRowAxisS(request);
			}
		}
		/// <summary>
		/// Připraví data pro graf TaskCapacityLink poté, kdy uživatel vstoupil na řádek Axis.
		/// </summary>
		/// <param name="request"></param>
		private void _FindInterGraphWorkForRowAxisS(DataSourceRequestFindInterGraph request)
		{
			// Aktivní řádek (task / osa)
			if (request.SourceDataRow == null) return;
			GID rowGId = request.SourceDataRow.GId;
			if (rowGId.IsEmpty) return;



		}
		/// <summary>
		/// Řeší mezigrafovou aktivitu pro událost MouseClick: vyhledá párové elementy z partnerského grafu
		/// </summary>
		/// <param name="request"></param>
		private void _FindInterGraphMouseClick(DataSourceRequestFindInterGraph request)
		{
			// Pokud je mezigrafový vztah definován takto: Zdroj = TaskCapacityLink z elementu na řádku Kapacitní plánovací jednotka,
			//     a Cíl (tj. graf, v němž bychom měli rozsvítit elementy) = Gantt s řádky z plánovací osy S:
			if (request.SourceGraphMode == RowGraphMode.TaskCapacityLink && request.SourceElement.Row.ClassNumber == PlanUnitCCls.ClassNr &&
				request.DefaultGraphMode == RowGraphMode.Gantt && request.GraphDeclaration.RowClassNumber == PlanUnitSAxisCls.ClassNr)
			{
				this._FindInterGraphAxisSForElementWork(request);
			}
			// Pokud jde o vztah z gantu, ze záznamu osy S typu SubAxis (Constants.ClassNumberAxisSubRow), 
			//     a Cíl je TaskCapacityLink z elementu na řádku Kapacitní plánovací jednotka,
			//     pak bych mohl zkusit najít odpovídající WorkUnits:
			else if (request.SourceGraphMode == RowGraphMode.Gantt && (request.SourceElement.Row.ClassNumber == Constants.ClassNumberAxisSubRow || request.SourceElement.Row.ClassNumber == Constants.ClassNumberAxis) &&
				request.DefaultGraphMode == RowGraphMode.TaskCapacityLink && request.GraphDeclaration.RowClassNumber == PlanUnitCCls.ClassNr)
			{
				this._FindInterGraphWorkForElementAxisS(request);
			}
		}
		/// <summary>
		/// Vyhledá párové elementy pro zdroj = TaskCapacityLink a cíl = Gantt osy S
		/// </summary>
		/// <param name="request"></param>
		private void _FindInterGraphAxisSForElementWork(DataSourceRequestFindInterGraph request)
		{
			if (request.SourceElement.Element.IsEmpty) return;                // Pokud není vybrán element, skončíme.

			switch (request.SourceElement.Element.ClassNumber)
			{
				case Constants.ClassNumberWork:
					request.ResultSelectElementList = this.PlanningData.FindPointersToTask(request.SourceElement.Element);
                    request.ResultRowFilterList = this.PlanningData.FindFinalAxisGidForTask(request.ResultSelectElementList);         // DAJ 0043102: aktivujeme řádkový filtr na finálové řádky grafu Gantt = osa S pro vybrané elementy
					break;
			}
		}
		/// <summary>
		/// Vyhledá párové elementy pro zdroj = AxisSSub a cíl = TaskCapacityLink
		/// </summary>
		/// <param name="request"></param>
		private void _FindInterGraphWorkForElementAxisS(DataSourceRequestFindInterGraph request)
		{
			GID elementGId = request.SourceElement.Element;
			if (elementGId.IsEmpty) return;                          // Pokud není vybrán element, skončíme.

			switch (request.SourceElement.Row.ClassNumber)           // Číslo třídy řádku, na němž uživatel kliknul na element.
			{
				case Constants.ClassNumberAxisSubRow:
					// Uživatel kliknul na element na řádku třídy SubAxis = výrobní postup (dílec), tedy element odpovídá tasku:
					//request.TargetElementList = this.PlanningData.FindPointersToTask(workID);
					request.ResultSelectElementList = this.PlanningData.FindPointersToWorkUnit(elementGId);
					break;
				case Constants.ClassNumberAxis:
					// Uživatel kliknul na element na řádku třídy MainAxis = poptávka, výrobní příkaz, minimální zásoba, tedy element odpovídá příjmu z výroby:
					request.ResultSelectElementList = this.PlanningData.FindPointersToWorkUnit(elementGId);
					break;
			}
		}
		#endregion
		#region INTERAKTIVNÍ EDITACE - ŘÍZENÍ POVOLENÍ, PŘENOS DAT Z VIZUÁLNÍ VRSTVY DO PROCESU
        /// <summary>
		/// Umožní zablokovat editaci, která právě začíná
		/// </summary>
		/// <param name="requestInput">Vstupující požadavek</param>
        private void _BeforeEditHandler(DataSourceRequest requestInput)
        {
            if (this.PlanningData.CurrentStateCanGetData)
            {
                DataSourceRequestEventBeforeEdit request = DataSourceRequest.TryGetTypedRequest<DataSourceRequestEventBeforeEdit>(requestInput);
                // Nejdřív musí být rozbočka podle typu grafu!!!   Sem mohou teoreticky přijít i žádosti o změny na dalších grafech...
                switch (request.DefaultGraphMode)
                {
                    case RowGraphMode.TaskCapacityLink:
                        this._BeforeEditTaskLink(request);
                        break;
                }
            }
        }
		/// <summary>
		/// Do plánovacího procesu vloží změnu definovanou v argumentu
		/// </summary>
		/// <param name="requestInput">Vstupující požadavek</param>
		private void _AfterEditHandler(DataSourceRequest requestInput)
		{
			if (this.PlanningData.CurrentStateCanGetData)
			{
				DataSourceRequestEventAfterEdit request = DataSourceRequest.TryGetTypedRequest<DataSourceRequestEventAfterEdit>(requestInput);
				// Nejdřív musí být rozbočka podle typu grafu!!!   Sem mohou teoreticky přijít i žádosti o změny na dalších grafech...
				switch (request.DefaultGraphMode)
				{
					case RowGraphMode.TaskCapacityLink:
						this._AfterEditTaskLink(request);
						break;
				}
			}
		}
		/// <summary>
		/// Zjistí, zda v dané situaci lze editovat data grafu TaskLink
		/// </summary>
		/// <param name="request">Požadavek na zmenu</param>
        private void _BeforeEditTaskLink(DataSourceRequestEventBeforeEdit request)
        {
            string errorInfo;
            request.EditEnabled = this.PlanningData.IsInteractiveEditEnabled(out errorInfo);
            if (!request.EditEnabled)
                request.InfoText = errorInfo;
        }
		/// <summary>
		/// Do plánovacího procesu vloží změnu definovanou v argumentu
		/// </summary>
		/// <param name="request">Požadavek na zmenu</param>
		private void _AfterEditTaskLink(DataSourceRequestEventAfterEdit request)
		{
            this.PlanningData.SetPlanningProcessState(PlanningProcessState.NowEditing);

			// Přesun zajistí plánovací proces. Připravím pro něj data, parametry podle konfigurace:
			PlanningInteractiveMoveArgs moveArgs = new PlanningInteractiveMoveArgs();
			moveArgs.CapacityLimitForActiveTask = LimitedCType.Unlimited;
            moveArgs.AutoMoveLinkedTask = AutoMoveLinkedTaskMode.MoveWhenSynchronized;         // Dohodnout analyticky a uživatelsky (v jakém režimu se pohybují nevybrané linkované operace)
			//moveArgs.SetFixedTask = true;
			moveArgs.PullAdjacentForActiveTask = true;
			moveArgs.PullAdjacentForActiveTree = true;

			// Načtu editované elementy:
			foreach (IDataElement element in request.ActiveElementList)
			{
				PlanningInteractiveMoveActiveItem moveItem = new PlanningInteractiveMoveActiveItem(element);
				// Změna KPJ => automaticky bude změna zdroje: (na konfiguraci)
				if (moveItem.IsChangedUnit)
					moveItem.CapacitySourceCurrent = this.PlanningData.FindTopCapacitySourceForUnit(moveItem.CapacityUnitCurrent);
				moveArgs.AddActiveItem(moveItem);
			}
			if (moveArgs.ActiveItemList.Count == 0)
				return;

			// Zavolám metodu pro interaktivní přeregistraci kapacity:
			this.PlanningData.InteractiveMove(moveArgs);

			// Výsledky přeplánování dat: přenesu pointery do requestu jako ResultEdit*:
            moveArgs.ResultCopyTo(request);
		}
		#endregion
		#region PODPŮRNÉ METODY
		/// <summary>
		/// Vygeneruje barvu pro element grafu.
		/// </summary>
		/// <param name="graphType">Typ grafu</param>
		/// <param name="visualType">Typ elementu. Jde o hodnotu PlanningVisualDataElementCls.ElementVisualType</param>
		/// <returns>Barva</returns>
		Color IDataSource.GetColorForElement(RowGraphMode graphType, GraphElementVisualType visualType)
		{
			switch (graphType)
			{
				case RowGraphMode.Gantt:
				case RowGraphMode.TaskCapacityLink:
					switch (visualType)
					{	// Tyto barvy odpovídají druhu práce / typu položky plánu.
						// Jejich využití je na dohodě mezi generátorem elementů, jaký vizuální typ do elementu vepíše,
						// a touto metodou, jakou barvu k tomuto typu přiřadí.
						case GraphElementVisualType.Type1:
							// Reálné výrobní příkazy:
							return Color.DarkSlateBlue;             // Color color = Color.FromArgb(72, 61, 139)
						case GraphElementVisualType.Type2:
							// Operace skutečné, navázané na výrobní příkaz:
							return Color.FromArgb(106, 93, 220);
						case GraphElementVisualType.Type3:
							// Rezerva:
							return Color.DarkRed;
						case GraphElementVisualType.Type4:
							// Návrhy na výrobní příkazy:
							return Color.Olive;                     // Color color = Color.FromArgb(128, 128, 0)
						case GraphElementVisualType.Type5:
							// Operace plánované:
							return Color.FromArgb(190, 190, 68);
						case GraphElementVisualType.Type6:
							return Color.DarkSalmon;
						case GraphElementVisualType.Type7:
							return Color.DarkCyan;
						case GraphElementVisualType.Type8:
							return Color.DarkGreen;
						case GraphElementVisualType.Type9:
							return Color.DarkOrange;
					}
					return Color.DarkBlue;

				case RowGraphMode.CapacityUtilization:
					switch (visualType)
					{	// Tyto barvy odpovídají směnám:
						// Jejich využití je na dohodě mezi generátorem elementů, jaký vizuální typ do elementu vepíše,
						// a touto metodou, jakou barvu k tomuto typu přiřadí.
						case GraphElementVisualType.Type1:
							return Color.FromArgb(128, 220, 255, 220);
						case GraphElementVisualType.Type2:
							return Color.FromArgb(128, 220, 255, 255);
						case GraphElementVisualType.Type3:
							return Color.FromArgb(128, 255, 220, 255);
						case GraphElementVisualType.Type4:
							return Color.FromArgb(128, 255, 255, 220);
						case GraphElementVisualType.Type5:
							return Color.FromArgb(128, 255, 255, 255);
						case GraphElementVisualType.Type6:
							return Color.FromArgb(128, 192, 220, 220);
						case GraphElementVisualType.Type7:
							return Color.DarkCyan;
						case GraphElementVisualType.Type8:
							return Color.DarkGreen;
						case GraphElementVisualType.Type9:
							return Color.DarkOrange;
					}
					return Color.FromArgb(128, 192, 255, 192);
			}
			return Color.DarkBlue;
		}
		/// <summary>
		/// Metoda, která má za úkol vrátit soupis GID vedlejších prvků k danému prvku.
		/// Tato metoda se použije pouze v procesu editace, kdy editovaný blok dat (sada operací, atd) neobsahuje všechny prvky, na které se odkazuje.
		/// Typicky to nastane v situaci, kdy prvek 30 se odkazuje na následující prvek 40, ale ten neexistuje.
		/// Tato metoda pak musí vrátit GID prvku, který má navazovat na prvek 40 => tj. typicky půjde o prvek 50.
		/// Pokud elementy, vrácené metodou RunRequestXxx(DataSourceRequestType.ElementsRead) vrací kompaktní skupiny dat, kde nejsou díry, 
		/// pak zdejší metoda GetNearbyGIDs() může vždy vracet null.
		/// </summary>
		/// <param name="gID">GID výchozího prvku</param>
		/// <param name="orientation">Směr, v němž hledáme další prvky</param>
		/// <returns>Soupis GIDů, které v daném směru navazují. Může být null.</returns>
		IEnumerable<GID> IDataSource.GetNearbyGIDs(GID gID, ElementRelationOrientation orientation)
		{
			return null;
		}
		#endregion
	}
	#region CLASS PlanningVisualDataRowCls : jeden řádek s daty
	/// <summary>
	/// Jedna položka, která popisuje záhlaví jednoho řádku libovolného seznamu
	/// </summary>
	public class PlanningVisualDataRowCls : IDataRow
	{
		#region PROPERTY
		/// <summary>
		/// Globální identifikátor tohoto záznamu.
		/// </summary>
		public GID GId { get { return _GId; } }
		/// <summary>
		/// Reference záznamu (zakázky, výrobního příkazu, operace, pracoviště, kapacitní jednotky, ...)
		/// </summary>
		public string Refer { get { return _Refer; } set { _Refer = value; } }
		/// <summary>
		/// Název záznamu (zakázky, výrobního příkazu, operace, pracoviště, kapacitní jednotky, ...)
		/// </summary>
		public string Name { get { return _Name; } set { _Name = value; } }
		/// <summary>
		/// Příznak, zda tento řádek má své pod-uzly (subnodes), tj. můžeme pro něj zobrazit ikonku [+] ve stromu
		/// </summary>
		public bool HasSubNodes { get { return _HasSubNodes; } set { _HasSubNodes = value; } }
		/// <summary>
		/// Druh grafu, který je pro tento řádek zobrazen
		/// </summary>
		public RowGraphMode RowGraph { get { return _RowGraph; } set { _RowGraph = value; } }
		/// <summary>
		/// Násobek obvyklé výšky řádku pro tuto položku.
		/// Default = 1 pro běžné textové řádky.
		/// </summary>
		public float RowHeightMultiplier
		{
			get { return _RowHeightMultiplier; }
			set { _RowHeightMultiplier = value; }
		}
		/// <summary>
		/// Globální identifikátor záznamu parenta.
		/// Typicky se parent nevepisuje: systém typicky funguje tak, že parent je ten objekt, pro který jsou načítány jeho podřízené prvky.
		/// ParentGId lze setovat. Vizuální vrstva jej přebírá po vrácení dat, ale pro svoji další interní práci si uchovává ParentGId jinde.
		/// Vztah na Parenta je tedy řešen na úrovni grafiky, která požádala o načtení podřízených záznamů.
		/// Nicméně je možno při načítání podřízených záznamů rovnou načíst i hlubší podřízené záznamy, a jim pak vyplnit ParentGId na explicitní záznam.
		/// Například: máme načíst všechny operace pro jeden výrobní příkaz.
		/// Načteme 6 operací, a do nich nevyplňujeme ParentGId (což je defaultní chování).
		/// Nad rámec operací se rozhodneme načíst i komponenty operací, tedy podřízené řádky.
		/// Pak pro každou ze 6 operací najdeme její komponenty, načteme je, do každé komponenty vložíme ParentGId = GId její operace, 
		/// a komponenty přidáme do výsledného seznamu dat. Sestavení struktury stromu pak zajistí vizuální vrstva.
		/// Toto je navíc i rekurzivní možnost.
		/// </summary>
		public GID ParentGId { get { return _ParentGId; } set { _ParentGId = value; } }
		/// <summary>
		/// Pole elementů (časové položky grafu), které jsou načítány společně s řádkem.
		/// Ve výchozím stavu je null !
		/// Některé datové typy jsou konstruovány tak, že časová položka je nedílnou součástí řádku 
		/// (např. operace výrobního příkazu je současně řádkem grafu, a současně časovou položkou). 
		/// Pak je vhodné vygenerovat časové položky společně s položkou řádku, a uložit informace do tohoto pole.
		/// Metoda GetRows tak vyřeší načítání jak řádku, tak časových elementů.
		/// Pokud jsou touto metodou načteny všechny časové úseky (např. může existovat jen jeden čas, a ten je uložen do tohoto pole),
		/// pak je třeba nastavit this.AllElementsLoaded = true, a řídící jednotka grafu pak nebude nikdy donačítat data pro širší časové úseky.
		/// Tento mechanismus se nehodí pro objekty, které mají časově široké rozmístění mnoha elementů, typicky stavy kapacit.
		/// </summary>
		public List<IDataElement> ElementList
		{
			get
			{
				if (_ElementList == null)
					_ElementList = new List<IDataElement>();
				return _ElementList;
			}
			set { _ElementList = value; }
		}
		/// <summary>
		/// Příznak, že elementy načtené do pole this.ElementList představují úplnou sadu elementů, 
		/// a pro tento řádek (this) nebude nikdy zapotřebí donačítat elementy pro širší časové pole.
		/// </summary>
		public bool AllElementsLoaded { get { return _AllElementsLoaded; } set { _AllElementsLoaded = value; } }
		/// <summary>
		/// Příznak, že elementy načtené do pole this.ElementList představují úplnou sadu elementů pro určitý (tento) WorkGID.
		/// Víc elementů pro tento WorkGID neexistuje. Typicky se nastavuje na neprázdný WorkGID při iniciaci dat osy S nebo osy C,
		/// kde se načítá jen jeden element pro jeden konkrétní WorkGID.
		/// Zůstává Empty při načítání elementů kapacitní jednotky, kde nelze předem načíst celý WorkList.
		/// </summary>
		public GID WorkGIDAllElementsLoaded { get { return _WorkGIDAllElementsLoaded; } set { _WorkGIDAllElementsLoaded = value; } }
		/// <summary>
		/// Akce grafické vrstvy po vstupu na řádek, typicky None
		/// </summary>
		public RowActionType ActionOnEnter { get { return this._ActionOnEnter; } set { this._ActionOnEnter = value; } }
		/// <summary>
		/// Akce grafické vrstvy po kliknutí na řádek, typicky None
		/// </summary>
		public RowActionType ActionOnClick { get { return this._ActionOnClick; } set { this._ActionOnClick = value; } }
		/// <summary>
		/// Akce grafické vrstvy po double kliknutí na řádek, typicky ZoomTimeToAllElements
		/// </summary>
		public RowActionType ActionOnDoubleClick { get { return this._ActionOnDoubleClick; } set { this._ActionOnDoubleClick = value; } }
        /// <summary>
        /// Deklarace vlastnsotí řádku.
        /// Může být null, pak se kompletně přebírá ze stejnojmenné property v deklaraci grafu (GraphDeclarationCls.RowGraphDeclaration).
        /// </summary>
        public DataRowGraphDeclaration RowGraphDeclaration { get { return this._RowGraphDeclaration; } set { this._RowGraphDeclaration = value; } }
		/// <summary>
		/// Ikona pro tento řádek
		/// </summary>
		public Image Icon
		{
			get
			{
				return null;
//				if (this._Icon == null) this._Icon = DataSourceMfrPlan.GetIconForGId(this.GId);
//				return this._Icon;
			}
		}
		/// <summary>
		/// Vrátí text, který se zobrazuje v ToolTipu.
		/// Může vrátit null, pak se za tento objekt nic nezobrazí.
		/// Může vrátit text, obsahující CrLf, pak bude text odřádkován.
		/// </summary>
		public string ToolTipInfo()
		{
			string result = String.Join(": ", new string[] { this.Refer, this.Name });

			// TextNote:
			string eols = "\r\n      ";
			if (this.TextNote != null)
				result += eols + this.TextNote.Replace("\r\n", eols);
			return result;
		}
		#endregion
		#region KONSTRUKTORY A VIZUALIZACE
		public PlanningVisualDataRowCls()
		{
			this._GId = new GID();
			this.Refer = null;
			this.Name = null;
			this.HasSubNodes = false;
			this.TextNote = "";
		}
		public PlanningVisualDataRowCls(GID gid, RowGraphMode rowGraph, string refer, string name)
		{
			this._GId = gid;
			this.RowGraph = rowGraph;
			this.Refer = refer;
			this.Name = name;
			this.HasSubNodes = false;
			this.TextNote = "";
		}
		public PlanningVisualDataRowCls(GID gid, RowGraphMode rowGraph, string refer, string name, float rowHeightMultiplier)
		{
			this._GId = gid;
			this.RowGraph = rowGraph;
			this.Refer = refer;
			this.Name = name;
			this.HasSubNodes = false;
			this.RowHeightMultiplier = rowHeightMultiplier;
			this.TextNote = "";
		}
		public PlanningVisualDataRowCls(GID gid, RowGraphMode rowGraph, string refer, string name, bool hasSubNodes)
		{
			this._GId = gid;
			this.RowGraph = rowGraph;
			this.Refer = refer;
			this.Name = name;
			this.HasSubNodes = hasSubNodes;
			this.TextNote = "";
		}
		public PlanningVisualDataRowCls(GID gid, RowGraphMode rowGraph, string refer, string name, bool hasSubNodes, string description)
		{
			this._GId = gid;
			this.RowGraph = rowGraph;
			this.Refer = refer;
			this.Name = name;
			this.HasSubNodes = hasSubNodes;
			this.TextNote = (String.IsNullOrEmpty(description) ? String.Empty : description);
		}
		/// <summary>
		/// Vizualizace
		/// </summary>
		/// <returns></returns>
		public override string ToString()
		{
			return this.GId.ToString() + ":[" + (this.Refer == null ? "" : this.Refer) + ": " + (this.Name == null ? "" : this.Name) + "]";
		}
		#endregion
		#region FIELDS
		private GID _GId;
		private GID _ParentGId;
		private Image _Icon;
		private string _Refer;
		private string _Name;
		private string _Description;
		private bool _HasSubNodes;
		private RowGraphMode _RowGraph;
		private List<IDataElement> _ElementList;
		private bool _AllElementsLoaded;
		private GID _WorkGIDAllElementsLoaded;
		private RowActionType _ActionOnEnter = RowActionType.None;
		private RowActionType _ActionOnClick = RowActionType.None;
		private RowActionType _ActionOnDoubleClick = RowActionType.None;
		private float _RowHeightMultiplier = 1F;
		/// <summary>
		/// Může obsahovat rozšiřující text, který bude vracet metoda ToolTip.
		/// </summary>
		public string TextNote;
        private DataRowGraphDeclaration _RowGraphDeclaration = null;
		#endregion
		#region SORT
		/// <summary>
		/// Komparátor podle Refer, Name
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <returns></returns>
		public static int CompareByLevelReferNazev(IDataRow a, IDataRow b)
		{
			if (a == null && b == null) return 0;
			if (a == null) return -1;
			if (b == null) return 1;

			// Level: v seznamu musí být nejdřív prvky bez parenta, a teprve po nich prvky s parentem.
			// A toto zjednodušení platí jen tehdy, když existují jen dvě úrovně stromu (základní a první vnořená).
			int aLevel = (a.ParentGId.IsEmpty ? 0 : 1);       // prvek (a) je v úrovni 0 (bez parenta) / 1 (s parentem)
			int bLevel = (b.ParentGId.IsEmpty ? 0 : 1);       // prvek (b) je v úrovni 0 (bez parenta) / 1 (s parentem)
			int cmp = aLevel.CompareTo(bLevel);               // Nižší hodnota má být dříve.
			if (cmp == 0 && aLevel == 0)
			{	// Pokud jsou obě položky ve shodné úrovni, a to v úrovni 0 (základní), srovnáme je podle reference a názvu:
				//  (pokud jsou obě položky v úrovni 1, nebudeme je nijak řadit)
				if (cmp == 0)
					cmp = String.Compare(a.Refer, b.Refer, StringComparison.CurrentCultureIgnoreCase);
				if (cmp == 0)
					cmp = String.Compare(a.Name, b.Name, StringComparison.CurrentCultureIgnoreCase);
			}
			return cmp;
		}
		#endregion
	}
	#endregion
	#region CLASS PlanningVisualDataElementCls : jeden prvek grafu, jeho logická data
	/// <summary>
	/// Jedna položka, která popisuje jednu položku grafu.
	/// Obsahuje informaci o parent GID = řádek, do něhož patří, obsahuje svůj GID = specifikuje svoji třídu, 
	/// obsahuje časový úsek v rámci grafu a další informace o způsobu využití uvedeného času.
	/// Neobsahuje nic grafického ve smyslu rozměrů, pozice, barvy, ikony atd.
	/// </summary>
    public class PlanningVisualDataElementCls : IDataElement, IConfiguredText
	{
		#region PROPERTY
		/// <summary> { get; }
		/// Globální identifikátor záznamu = tohoto elementu
		/// </summary>
		public GID GId { get { return _GId; } }
        /// <summary>
        /// Číslo třídy, pro kterou se budou konfigurovat texty elementu (tooltip + caption)
        /// </summary>
        public int RootClassForToolTip { get { return _RootClassForToolTip; } set { _RootClassForToolTip = value; } }
        /// <summary>
        /// Suffix klíče pro ToolTip, přidá se k číslu třídy (pokud bude zadáno).
        /// </summary>
        public string SuffixKeyForToolTip { get { return _SuffixKeyForToolTip; } set { _SuffixKeyForToolTip = value; } }
        /// <summary> { get; set; }
		/// Globální identifikátor rodičovského záznamu = řádek, v kterém se element nachází.
		/// Pokud je toto čas operace, pak parentem je operace.
		/// Pokud tento element je čas stavu kapacit, jde o kapacitní jednotku.
		/// Pokud tento element představuje část práce z konkrétní operace na konkrétní kapacitě, pak bude zobrazen na dané kapacitě.
		/// Jde o aktuální údaj, který může obsahovat editovanou hodnotu.
		/// </summary>
		public GID RowGId { get { return _RowGId; } set { _RowGId = value; } }
		/// <summary> { get; }
		/// Globální identifikátor rodičovského záznamu = řádek, v kterém se element nachází.
		/// Jde o původní údaj, načtený z databáze. Grafický element jej nemůže změnit.
		/// </summary>
		public GID OriginalRowGId { get { return _OriginalRowGId; } }
		/// <summary> { get; }
		/// Globální identifikátor pracovní jednotky: jedna pracovní jednotka může být zobrazena ve více grafech (různé typy grafů),
		/// a v rámci jednoho grafu může být jedna práce zobrazena ve více časových elementech (časově roztrhaná práce), ale přesto je to jeden pracovní úkol.
		/// </summary>
		public GID WorkGId { get { return _WorkGId; } }
		/// <summary>
		/// Globální identifikátor nadřízené pracovní jednotky: původce tohoto pracovního úseku.
		/// Pokud tento element je práce na operaci, pak parentem je výrobní příkaz/dílec.
		/// Pokud tento element je dílec, pak parentem může být zakázka, nebo nic.
		/// Pokud tento element je stav kapacit, pak parenta nemá: do parenta se promítají změny v čase této operace, a stav kapacit nelze změnit přetáhnutím.
		/// </summary>
		public GID ParentWorkGId { get { return _ParentWorkGId; } set { _ParentWorkGId = value; } }
		/// <summary>
		/// Globální identifikátor podřízených záznamů. Obsahuje WorkGId záznamů elementů, které jsou podřízené tomuto elementu.
		/// Lze tak sestavit mapu (strom) 
		/// </summary>
		public GID[] FirstChildWorkGIds { get { return _FirstChildWorkGIds; } set { _FirstChildWorkGIds = value; } }
		/// <summary> { get; set; }
		/// Globální identifikátor WorkGId pracovních úkolů, které předcházejí výkonu práce na elementech this.WorkGId
		/// </summary>
		public GID[] PreviousWorkGIds { get { return _PreviousWorkGIds; } set { _PreviousWorkGIds = value; } }
		/// <summary> { get; set; }
		/// Globální identifikátor WorkGId pracovních úkolů, které následují po výkonu práce na elementech this.WorkGId
		/// </summary>
		public GID[] NextWorkGIds { get { return _NextWorkGIds; } set { _NextWorkGIds = value; } }
		/// <summary> { get; set; }
		/// Časový interval tohoto elementu (od kdy do kdy trvá).
		/// Jde o aktuální údaj, který může obsahovat editovanou hodnotu.
		/// </summary>
		public TimeRange TimeRange { get { return _TimeRange; } set { _TimeRange = value; } }
		/// <summary> { get; }
		/// Časový interval tohoto elementu (od kdy do kdy trvá).
		/// Jde o původní údaj, načtený z databáze. Grafický element jej nemůže změnit.
		/// </summary>
		public TimeRange OriginalTimeRange { get { return _OriginalTimeRange; } }
		/// <summary>
		/// Zlomky elementu, převezme si je vhodný painter
		/// </summary>
		public float[] Fragments { get { return this._Fragments; } set { this._Fragments = value; } }
		/// <summary> { get; }
		/// Příslušnost data do vrstvy.
		/// Odlišuje vrstvu podkladovou (typicky zobrazení směny) a vrstvu datovou (aktivita v čase, která může být delší / kratší než směna).
		/// </summary>
		public GraphElementLayerType ElementLayer { get { return _ElementLayer; } }
		/// <summary> { get; }
		/// Definuje režim, jakým lze touto položkou pohybovat ( Move a Resize )
		/// a jaký vliv má tento pohyb na okolní položky ( nadřízené a sousední ).
		/// Tato proměnná má význam pouze pro vrstvu (ElementLayer == GraphElementLayerType.ItemLayer).
		/// </summary>
		public DataElementEditingMode ElementEditingMode { get { return _ElementEditingMode; } }
		/// <summary>
		/// Tvar elementu.
		/// </summary>
		public GraphElementShapeType ElementShapeType { get { return this._ElementShapeType; } set { this._ElementShapeType = value; } }
		/// <summary>
		/// Barva pozadí elementu
		/// </summary>
		public Color BackColor { get { return this._BackColor; } set { this._BackColor = value; } }
		/// <summary>
		/// Barva písma v elementu
		/// </summary>
		public Color FontColor { get { return this._FontColor; } set { this._FontColor = value; } }
		/// <summary> { get; }
		/// Specifikuje druh obsahu, ovlivňuje způsob zobrazování (barvu, vzhled).
		/// Datová vrstva zde může odlišovat různé typy směn, různé režimy práce, různé úrovně hierarchie zakázky, různé zobrazované třídy.
		/// U grafu typu RowGraphMode.Gantt ovlivní barvu políčka, vyjadřuje druh činnosti.
		/// U grafu typu RowGraphMode.CapacityUtilization ovlivní barvu políčka, vyjadřuje druh směny.
		/// U grafu typu RowGraphMode.TaskCapacityLink ovlivňuje barvu políčka směny (pokud je this.ElementLayer == SubLayer), nebo práce (pokud je this.ElementLayer == ItemLayer)
		/// Konkrétní barvu generuje metoda IDataSource.GetColorForElement()
		/// </summary>
		public GraphElementVisualType ElementVisualType { get { return _ElementVisualType; } }
        /// <summary>
        /// Ikona vykreslovaná v elementu.
        /// Ikona se kreslí tak, že její střed leží na středu času Begin.
        /// Může být null.
        /// </summary>
        public Image Icon { get; set; }
        /// <summary> { get; }
		/// Splnění úkolu, typicky u operací, které jsou částečně nebo zcela odvedeny.
		/// Zohledňuje se u grafu typu Gantt. (Typ grafu určuje RowGId (typ řádku).)
		/// Smysluplné hodnoty: 0 (= ještě se ani nezačalo) až 1 (= vše je hotovo). Hodnoty mimo rozsah 0 až 1 mají význam 0 nebo 1.
		/// </summary>
		public float CompletionRatio { get { return _CompletionRatio; } }
		/// <summary> { get; }
		/// Využití stavu kapacity v poměrné hodnotě (1.00 = 100%). Záporné hodnoty mají význam jako 0. 
		/// Hodnoty nad 1.00 značí přečerpání kapacit a zobrazují se.
		/// </summary>
		public float UtilizationRatio { get { return _UtilizationRatio; } }
		/// <summary>
		/// Poměrné využití výrobní linky pro tuto operaci (podílové využití)
		/// </summary>
		public float UseRatio { get { return _UseRatio; } set { _UseRatio = value; } }
		/// <summary> { get; set; }
		/// Číslo linkovací skupiny, typicky číslo subjektu ve vztahu "lcs.plan_unit_c_task.link".
		/// Hodnota 0 = není link, tento záznam není nijak spojen s jinými záznamy.
		/// </summary>
		public int LinkGroup { get { return _LinkGroup; } set { _LinkGroup = value; } }
		/// <summary>
		/// Vrátí text, který se zobrazuje v ToolTipu.
		/// Může vrátit null, pak se za tento objekt nic nezobrazí.
		/// Může vrátit text, obsahující CrLf, pak bude text odřádkován.
		/// </summary>
		public string ToolTipInfo(IDataRow rowData)
		{
			// Podle typu grafu:
			string timeRange = "Čas od: " + this.TimeRange.Begin.ToString() + "  do: " + this.TimeRange.End.ToString();
			string eol = "\r\n";
			string result = null;
			double percent;
			if (this.ElementLayer == GraphElementLayerType.TempLayer)
				result = timeRange;
			else
			{
				switch (rowData.RowGraph)
				{
					case RowGraphMode.CapacityUtilization:
						percent = Math.Round(this.UtilizationRatio * 100F, 1);
						result = timeRange + eol + "Kapacita využita na " + percent.ToString() + "%";
						break;
					case RowGraphMode.Gantt:
						result = timeRange;
						percent = Math.Round(this.CompletionRatio * 100F, 1);
						if (percent > 0F)
							result += eol + "Dokončeno " + percent.ToString() + "%";
						break;
					case RowGraphMode.TaskCapacityLink:
						result = timeRange;
						break;
				}
			}
			// TextNote:
			string eols = "\r\n      ";
			if (this.TextNote != null)
				result += eols + this.TextNote.Replace("\r\n", eols);

			return result;
		}
		/// <summary>
		/// Vrátí text, který se zobrazuje v obdélníku elementu při jeho "zaměření", například při editaci.
		/// Jde o krátkou, zásadně jednořádkovou informaci, jejíž délka (počet znaků) je omezena 
		/// fyzickou velikostí elementu při aktuálním rozlišení.
		/// </summary>
		/// <param name="rowData">Data o řádku, v němž se element nachází. Řádek může obsahovat některé potřebné informace.</param>
		/// <param name="charCount">Nejvyšší počet aktuálně zobrazitelných znaků. 
		/// Pokud bude vrácený text obsahovat více znaků, budou pravděpodobně oříznuty. 
		/// Pravděpodobnost je způsobena proporcionálním písmem. 
		/// Daný počet znaků je vypočten na maximální šířku znaku, jde tedy o garantovaný zobrazitelný počet. 
		/// Pokud bude vrácen text delší, ale složený z užších znaků, možná se podaří je zobrazit korektně. 
		/// Jsme v grafickém prostředí Windows...</param>
		/// <returns></returns>
		public string Caption(IDataRow rowData, float charCount)
		{
			string result = rowData.Name;

			if (this.TextCaptions == null) return result;

			foreach (string text in this.TextCaptions)
			{
				if (text == null) continue;
				string caption = text.Trim();

				if (caption.Length < charCount)
					result = caption;
				else
					break;
			}

			return result;
		}
        /// <summary>
        /// Režim viditelnosti elementu. Výchozí je Default (0): elementy Gantt jsou viditelné vždy, elementy TaskLink jsou viditelné jen pokud jsou nějak aktivní (myší, výběrem).
        /// </summary>
        public CaptionVisibilityMode CaptionVisibility { get { return this._CaptionVisibility; } set { this._CaptionVisibility = value; } }
        #endregion
		#region KONSTRUKTORY A VIZUALIZACE
		public PlanningVisualDataElementCls(GID gId, GID rowGId, int rootClassForToolTip, GID workGId, TimeRange timeRange)
		{
			this._GId = gId;
			this._RowGId = rowGId;
            this._RootClassForToolTip = rootClassForToolTip;
			this._OriginalRowGId = rowGId;
			this._WorkGId = workGId;
			this._TimeRange = timeRange;
			this._OriginalTimeRange = timeRange;
			this._ElementLayer = GraphElementLayerType.ItemLayer;
			this._ElementEditingMode = DataElementEditingMode.DefaultEditingState;
			this._ElementShapeType = GraphElementShapeType.Rectangle;
			this._ElementVisualType = DefaultVisualType;
			this._UtilizationRatio = 0F;
			this._CompletionRatio = 0F;
		}
        public PlanningVisualDataElementCls(GID gId, GID rowGId, int rootClassForToolTip, GID workGId, TimeRange timeRange, DataElementEditingMode moveMode, GraphElementShapeType elementShapeType)
		{
			this._GId = gId;
			this._RowGId = rowGId;
            this._RootClassForToolTip = rootClassForToolTip;
            this._OriginalRowGId = rowGId;
			this._WorkGId = workGId;
			this._TimeRange = timeRange;
			this._OriginalTimeRange = timeRange;
			this._ElementLayer = GraphElementLayerType.ItemLayer;
			this._ElementEditingMode = moveMode;
			this._ElementShapeType = elementShapeType;
			this._ElementVisualType = DefaultVisualType;
			this._UtilizationRatio = 0F;
			this._CompletionRatio = 0F;

		}
        public PlanningVisualDataElementCls(GID gId, GID rowGId, int rootClassForToolTip,  GID workGId, TimeRange timeRange, DataElementEditingMode moveMode)
		{
			this._GId = gId;
			this._RowGId = rowGId;
            this._RootClassForToolTip = rootClassForToolTip;
            this._OriginalRowGId = rowGId;
			this._WorkGId = workGId;
			this._TimeRange = timeRange;
			this._OriginalTimeRange = timeRange;
			this._ElementLayer = GraphElementLayerType.ItemLayer;
			this._ElementEditingMode = moveMode;
			this._ElementVisualType = DefaultVisualType;
			this._UtilizationRatio = 0F;
			this._CompletionRatio = 0F;

		}
        public PlanningVisualDataElementCls(GID gId, GID rowGId, int rootClassForToolTip, GID workGId, TimeRange timeRange, float completionRatio)
		{
			this._GId = gId;
			this._RowGId = rowGId;
            this._RootClassForToolTip = rootClassForToolTip;
            this._OriginalRowGId = rowGId;
			this._WorkGId = workGId;
			this._TimeRange = timeRange;
			this._OriginalTimeRange = timeRange;
			this._ElementLayer = GraphElementLayerType.ItemLayer;
			this._ElementEditingMode = DataElementEditingMode.DefaultEditingState;
			this._ElementVisualType = DefaultVisualType;
			this._UtilizationRatio = 0F;
			this._CompletionRatio = completionRatio;
		}
		/// <summary>
		/// Vizualizace
		/// </summary>
		/// <returns></returns>
		public override string ToString()
		{
			return "{Element GID=" + this.GId.ToString() + "; Parent=" + this.RowGId.ToString() + "; Time=" + this.TimeRange.ToString() + "}";
		}
		#endregion
		#region FIELDS
		private GID _GId;
        private int _RootClassForToolTip;
        private string _SuffixKeyForToolTip;
		private GID _RowGId;
		private GID _OriginalRowGId;
		private GID _WorkGId;
		private GID _ParentWorkGId;
		private GID[] _FirstChildWorkGIds;
		private GID[] _PreviousWorkGIds;
		private GID[] _NextWorkGIds;
		private TimeRange _TimeRange;
		private TimeRange _OriginalTimeRange;
		private float[] _Fragments;
		private GraphElementLayerType _ElementLayer;
		private DataElementEditingMode _ElementEditingMode;
		private GraphElementShapeType _ElementShapeType;
		private Color _BackColor;
		private Color _FontColor;
		private GraphElementVisualType _ElementVisualType;
		private float _CompletionRatio = 0F;
		private float _UtilizationRatio = 0F;
		private float _UseRatio = 1F;
		private int _LinkGroup;
		/// <summary>
		/// Může obsahovat rozšiřující text, který bude vracet metoda ToolTip.
		/// </summary>
		public string TextNote;
		/// <summary>
		/// Může obsahovat pole stringů, od nejkratšího po nejkomplexnější, které slouží jako zdroj popisku pro element v době jeho editace.
		/// Pokud je null, pak jako popisek bude sloužit text řádku.
		/// Pokud není null, mělo by pole obsahovat popisky v různé délce, počínaje nejkratším až po nejdelší.
		/// Metoda this.Caption() si pak vybere jeden text podle vhodné délky, pro aktuální velikost grafu.
		/// </summary>
		public string[] TextCaptions;
        private CaptionVisibilityMode _CaptionVisibility = CaptionVisibilityMode.Default;
        #endregion
		#region POMOCNÉ METODY
		/// <summary>
		/// Defaultní vizuální styl objektu, který se běžně použije v konstruktoru. Odvozuje vizuální styl podle třídy.
		/// Ve vytvářeném objektu je vložen do this.ElementVisualType. Tam je možno styl upravit.
		/// </summary>
		protected GraphElementVisualType DefaultVisualType
		{
			get
			{	// Vizuální typ = barevnost.
				// Může se odvíjet od (this._GId.NClass) = třída elementu (tj. např. čerpání stavu kapacit)
				// anebo od (this._RowGId.NClass) = třída řádku (tj. např. výrobní příkaz, plán, atd)
				switch (this._RowGId.ClassNumber)
				{
					case 0x04A4:
						return GraphElementVisualType.Type1;
					case 0x0501:
						return GraphElementVisualType.Type2;
					case 0x0502:
						return GraphElementVisualType.Type3;
					case 0x0480:
						return GraphElementVisualType.Type6;
				}
				return GraphElementVisualType.Type1;
			}
		}
		/// <summary>
		/// Nastaví vlastnosti elementu (neboť property jsou read-only)
		/// </summary>
		/// <param name="visualType">Vizuální typ (barevnost)</param>
		public void SetElementProperty(GraphElementVisualType visualType)
		{
			this._ElementVisualType = visualType;
		}
		/// <summary>
		/// Nastaví vlastnosti elementu (neboť property jsou read-only)
		/// </summary>
		/// <param name="elementShapeType">Tvar elementu</param>
		public void SetElementProperty(GraphElementShapeType elementShapeType)
		{
			this._ElementShapeType = elementShapeType;
		}
		/// <summary>
		/// Nastaví vlastnosti elementu (neboť property jsou read-only)
		/// </summary>
		/// <param name="editingMode">Typ editace</param>
		/// <param name="visualType">Vizuální typ (barevnost)</param>
		public void SetElementProperty(DataElementEditingMode editingMode, GraphElementVisualType visualType)
		{
			this._ElementEditingMode = editingMode;
			this._ElementVisualType = visualType;
		}
		/// <summary>
		/// Nastaví vlastnosti elementu (neboť property jsou read-only)
		/// </summary>
		/// <param name="layerType">Typ vrstvy elementu</param>
		/// <param name="editingMode">Typ editace</param>
		/// <param name="visualType">Vizuální typ (barevnost)</param>
		public void SetElementProperty(GraphElementLayerType layerType, DataElementEditingMode editingMode, GraphElementVisualType visualType)
		{
			this._ElementLayer = layerType;
			this._ElementEditingMode = editingMode;
			this._ElementVisualType = visualType;
		}
		/// <summary>
		/// Nastaví vlastnosti elementu (neboť property jsou read-only)
		/// </summary>
		/// <param name="layerType">Typ vrstvy elementu</param>
		/// <param name="editingMode">Typ editace</param>
		/// <param name="visualType">Vizuální typ (barevnost)</param>
		public void SetElementProperty(GraphElementLayerType layerType, DataElementEditingMode editingMode, GraphElementVisualType visualType, GraphElementShapeType elementShapeType)
		{
			this._ElementLayer = layerType;
			this._ElementEditingMode = editingMode;
			this._ElementShapeType = elementShapeType;
			this._ElementVisualType = visualType;
		}
		/// <summary>
		/// Nastaví vlastnosti elementu (neboť property jsou read-only)
		/// </summary>
		/// <param name="layerType">Typ vrstvy elementu</param>
		/// <param name="editingMode">Typ editace</param>
		/// <param name="visualType">Vizuální typ (barevnost)</param>
		/// <param name="completionRatio">Poměr splnění gantu</param>
		/// <param name="utilizationRatio">Poměr využití kapacity</param>
		public void SetElementProperty(GraphElementLayerType layerType, DataElementEditingMode editingMode, GraphElementVisualType visualType, float completionRatio, float utilizationRatio, float useRatio)
		{
			SetElementProperty(layerType, editingMode, visualType);
			this._UtilizationRatio = utilizationRatio;
			this._CompletionRatio = completionRatio;
			this._UseRatio = useRatio;
		}
		/// <summary>
		/// Nastaví barvu pozadí (a popředí)
		/// </summary>
		/// <param name="backColor">Barva pozadí, výplň buňky</param>
		public void SetColor(Color backColor)
		{
			this._BackColor = backColor;
		}
		/// <summary>
		/// Nastaví barvu pozadí (a popředí)
		/// </summary>
		/// <param name="backColor">Barva pozadí, výplň buňky</param>
		/// <param name="fontColor">Barva písma v buňce</param>
		public void SetColor(Color backColor, Color fontColor)
		{
			this._BackColor = backColor;
			this._FontColor = fontColor;
		}
		/// <summary>
		/// Nastaví zakotvení tohoto elementu do mapy okolních elementů.
		/// </summary>
		/// <param name="parentWorkGId">WorkGID parenta. Ten je jen jeden.</param>
		/// <param name="previousWorkGIds">GID[] předchozích WorkGID (ne elementy, ale tasky).</param>
		/// <param name="nextWorkGIds">GID[] návazných WorkGID (ne elementy, ale tasky).</param>
		/// <param name="firstChildWorkGIds">GID[] podřízených WorkGID (ne elementy, ale tasky).</param>
		public void SetElementNodes(GID parentWorkGId, GID[] previousWorkGIds, GID[] nextWorkGIds, GID[] firstChildWorkGIds)
		{
			this._ParentWorkGId = parentWorkGId;
			this._PreviousWorkGIds = previousWorkGIds;
			this._NextWorkGIds = nextWorkGIds;
			this._FirstChildWorkGIds = firstChildWorkGIds;
		}
		/// <summary>
		/// Uloží do své paměti texty, které se budou používat jako Caption při editaci elementu jako popisek v řádku.
		/// </summary>
		/// <param name="captions"></param>
		public void SetElementCaptions(params string[] captions)
		{
			this.TextCaptions = captions;
		}
		#endregion
        #region IConfiguredText Members
        bool IConfiguredText.EnabledToolTipTextConfigure { get { return true; } }
        bool IConfiguredText.EnabledCaptionTextConfigure{ get { return true; } }
        int IConfiguredText.RootClassNumber { get { return this.RootClassForToolTip; } }
        string IConfiguredText.ConfigurationKey { get { return this.RootClassForToolTip.ToString() + (String.IsNullOrEmpty(_SuffixKeyForToolTip) ? "" : "_" + _SuffixKeyForToolTip); } }
        #endregion
    }
	#endregion
	#region CLASS VisualTaskTimeCls : střadač / konvertor / exportér jednotlivých fází jedné výrobní operace, pro barevné zobrazení poměru TAC / ...
	/// <summary>
	/// VisualTaskTimeCls : střadač / konvertor / exportér jednotlivých fází jedné výrobní operace.
	/// Jeho účelem je nasčítávat časy fází (TMC, TBC, TAC, TEC) z různých operací, aby bylo možno zobrazovat 
	/// barevným proužkem poměrné využití času operace.
	/// </summary>
	internal class VisualTaskTimeCls
	{
		#region KONSTRUKCE
		private VisualTaskTimeCls(DateTime stockEnd)
		{
			this._SecondsTMC = 0D;
			this._SecondsTBC = 0D;
			this._SecondsTAC = 0D;
			this._SecondsTEC = 0D;
			this._TimeWork = TimeRange.Empty;
			this._StockEnd = stockEnd;
			this._Calculated = false;
		}
		/// <summary>Součet sekund fáze MoveTime</summary>
		private double _SecondsTMC;
		/// <summary>Součet sekund fáze TBC</summary>
		private double _SecondsTBC;
		/// <summary>Součet sekund fáze TAC</summary>
		private double _SecondsTAC;
		/// <summary>Součet sekund fáze TEC</summary>
		private double _SecondsTEC;
		/// <summary>Sloučený čas všech fází všech operací / průchodů, mimo čas TSC, který se nachází za _TimeWork.End do _StockEnd</summary>
		private TimeRange _TimeWork;
		/// <summary>Koncové datum pohybu = výsledná výdejka (do poptávky, do komponenty)</summary>
		private DateTime _StockEnd;
		/// <summary>Příznak, že výsledky _Result jsou platné</summary>
		private bool _Calculated;
		/// <summary>
		/// Vizualizace
		/// </summary>
		/// <returns></returns>
		public override string ToString()
		{
			return "{Time=" + this.TimeReal + "; Density=" + this.TotalDensity + "}";
		}
		#endregion
		#region REKALKULACE
		/// <summary>
		/// Zajistí, že budou vypočteny výsledky do proměnných _Result
		/// </summary>
		private void _CheckCalc()
		{
			if (this._Calculated) return;

			double secondsWork = this._SecondsTMC + this._SecondsTBC + this._SecondsTAC + this._SecondsTEC;
			TimeRange timeWork = this._TimeWork;
			if ((timeWork.IsEmpty || timeWork.Seconds <= 0D) && this._StockEnd > DateTime.MinValue)
			{	// Čas práce dosud není zadán, ale známe čas konce: vytoříme jej podle počtu sekund (nejspíš bude 0):
				timeWork = new TimeRange(this._StockEnd, this._StockEnd);
				if (secondsWork > 0D)
					// Pokud je počet sekund kladný, tak vytvořím časový interval od daného konce, určím odpovídající Begin:
					timeWork.SetSeconds(secondsWork, TimeRange.TimeDirection.ToHistory);
			}
			// Mám korektní timeWork, vytvoříme časy konců jednotlivých fází:
			double secondsKfc = (secondsWork > 0D ? timeWork.Seconds / secondsWork : 0D);     // Koeficient
			DateTime timeMB = timeWork.Begin;
			DateTime timeME = timeMB.AddSeconds((this._SecondsTMC) * secondsKfc);
			DateTime timeBE = timeMB.AddSeconds((this._SecondsTMC + this._SecondsTBC) * secondsKfc);
			DateTime timeAE = timeMB.AddSeconds((this._SecondsTMC + this._SecondsTBC + this._SecondsTAC) * secondsKfc);
			DateTime timeEE = timeWork.End;
			DateTime timeSE = (this._StockEnd > timeEE ? this._StockEnd : timeEE);

			this._TimeTMC = new TimeRange(timeMB, timeME);
			this._TimeTBC = new TimeRange(timeME, timeBE);
			this._TimeTAC = new TimeRange(timeBE, timeAE);
			this._TimeTEC = new TimeRange(timeAE, timeEE);
			this._TimeTSC = new TimeRange(timeEE, timeSE);
			this._TimeReal = new TimeRange(timeMB, timeSE);

			// Přznak OK:
			this._Calculated = true;
		}
		#endregion
		#region PROPERTY
		/// <summary>Součet počtu sekund času TMC</summary>
		internal float SecondsTMC { get { return (float)(this._SecondsTMC); } }
		/// <summary>Součet počtu sekund času TBC</summary>
		internal float SecondsTBC { get { return (float)(this._SecondsTBC); } }
		/// <summary>Součet počtu sekund času TAC</summary>
		internal float SecondsTAC { get { return (float)(this._SecondsTAC); } }
		/// <summary>Součet počtu sekund času TEC</summary>
		internal float SecondsTEC { get { return (float)(this._SecondsTEC); } }
		/// <summary>Čas konce operace/tasku/záznamu</summary>
		internal DateTime StockEnd { get { return this._StockEnd; } }
		/// <summary>Čas celkový, od počátku TMC do konce StockEnd</summary>
		internal TimeRange TimeReal { get { this._CheckCalc(); return this._TimeReal; } }
		private TimeRange _TimeReal;
		/// <summary>Čas fáze MoveTime</summary>
		internal TimeRange TimeTMC { get { this._CheckCalc(); return this._TimeTMC; } }
		private TimeRange _TimeTMC;
		/// <summary>Čas fáze TBC</summary>
		internal TimeRange TimeTBC { get { this._CheckCalc(); return this._TimeTBC; } }
		private TimeRange _TimeTBC;
		/// <summary>Čas fáze TAC</summary>
		internal TimeRange TimeTAC { get { this._CheckCalc(); return this._TimeTAC; } }
		private TimeRange _TimeTAC;
		/// <summary>Čas fáze TEC</summary>
		internal TimeRange TimeTEC { get { this._CheckCalc(); return this._TimeTEC; } }
		private TimeRange _TimeTEC;
		/// <summary>Čas fáze TSC</summary>
		internal TimeRange TimeTSC { get { this._CheckCalc(); return this._TimeTSC; } }
		private TimeRange _TimeTSC;
		/// <summary>
		/// Obsahuje součet sekund jednotlivých časů
		/// </summary>
		internal double TotalSeconds { get { return (this._SecondsTMC + this._SecondsTBC + this._SecondsTAC + this._SecondsTEC + this.TimeTSC.Seconds); } }
		/// <summary>
		/// Hustota práce v času
		/// </summary>
		internal double TotalDensity { get { return (this.TotalSeconds / this.TimeReal.Seconds); } }
		#endregion
		#region STATIC KONSTRUKTORY
		/// <summary>
		/// Vrátí vždy nový prázdný objekt
		/// </summary>
		internal static VisualTaskTimeCls Empty { get { return new VisualTaskTimeCls(DateTime.MinValue); } }
		/// <summary>
		/// Vytvoří a vrátí nový objekt a vyplní do něj okamžik konce
		/// </summary>
		/// <param name="task"></param>
		/// <param name="stockTimeRange"></param>
		/// <returns></returns>
		internal static VisualTaskTimeCls CreateFromEnd(DateTime stockEnd)
		{
			VisualTaskTimeCls time = new VisualTaskTimeCls(stockEnd);
			return time;
		}
		/// <summary>
		/// Vytvoří a vrátí nový objekt a naplní do něj data z předaného tasku
		/// </summary>
		/// <param name="task"></param>
		/// <param name="stockTimeRange"></param>
		/// <returns></returns>
		internal static VisualTaskTimeCls CreateFromTask(DateTime stockEnd, CapacityPlanWorkItemCls task)
		{
			VisualTaskTimeCls time = new VisualTaskTimeCls(stockEnd);
			time.AddTimeFromTask(task);
			return time;
		}
		#endregion
		#region PŘIDÁVÁNÍ ČASU
		/// <summary>
		/// Přidá do sebe časy z předané operace (projde všechny Pass)
		/// </summary>
		/// <param name="task"></param>
		internal void AddTimeFromTask(CapacityPlanWorkItemCls task)
		{
			foreach (WorkPassCls pass in task.WorkPassList)
				this.AddTimeFromPass(pass);
		}
		/// <summary>
		/// Přidá do sebe časy z předaného pracovního průchodu (jednotka operace)
		/// </summary>
		/// <param name="pass"></param>
		internal void AddTimeFromPass(WorkPassCls pass)
		{
			this._SecondsTMC += this._GetSeconds(pass.TimeTMC);
			this._SecondsTBC += this._GetSeconds(pass.TimeTBC);
			this._SecondsTAC += this._GetSeconds(pass.TimeTAC);
			this._SecondsTEC += this._GetSeconds(pass.TimeTEC);
		}
		/// <summary>
		/// Přidá do sebe časy z předaného jiného času
		/// </summary>
		/// <param name="pass"></param>
		internal void AddTimeFromTime(VisualTaskTimeCls time)
		{
			// Pokud ten druhý čas končí později, vezmu ten druhý čas:
			if (this._StockEnd < time._StockEnd) this._StockEnd = time._StockEnd;

			// Sečtu sekundy:
			this._SecondsTMC += time._SecondsTMC;
			this._SecondsTBC += time._SecondsTBC;
			this._SecondsTAC += time._SecondsTAC;
			this._SecondsTEC += time._SecondsTEC;

			// Sečtu časové okno Work:
			this._TimeWork = this._TimeWork + time._TimeWork;

			this._Calculated = false;
		}
		/// <summary>
		/// Zaregistruje časový interval operace do this._TimeWork, a vrátí počet sekund času
		/// </summary>
		/// <param name="timeRange"></param>
		/// <returns></returns>
		private double _GetSeconds(TimeRange timeRange)
		{
			this._TimeWork = this._TimeWork + timeRange;
			this._Calculated = false;
			return timeRange.Seconds;
		}
		#endregion
	}
	#endregion
}
